Bladeren bron

Add a new test case that fails

jherve 1 jaar geleden
bovenliggende
commit
e8d5b72ff2

+ 7 - 0
src/LinkedIn/DetachedNode.purs

@@ -28,6 +28,7 @@ import Yoga.Tree (Tree, leaf, mkTree)
 data DetachedNode =
   DetachedElement {tag :: String, content :: String, id :: Maybe String, classes :: List String}
   | DetachedSvgElement {tag :: String, id :: Maybe String, dataTestIcon :: Maybe String}
+  | DetachedImg {src :: String, classes :: List String}
   | DetachedLiIcon String
   | DetachedA {content :: String, href :: String}
   | DetachedButton {content :: String, role :: Maybe String, classes :: List String}
@@ -71,6 +72,12 @@ elementToDetached el = case E.tagName el of
     type_ <- unsafeGetAttribute "type" el
     pure $ DetachedLiIcon type_
 
+  "IMG" -> do
+    classes <- getClassList el
+    src <- unsafeGetAttribute "src" el
+
+    pure $ DetachedImg {classes, src}
+
   -- On SVG elements "className" returns a weird "SVGString" type that cannot be trimmed
   tag | tag == "svg" || tag == "use" || tag == "path" -> do
     id <- getId el

+ 3 - 1
src/LinkedIn/UI/Elements/Parser.purs

@@ -7,8 +7,8 @@ import Data.Either (Either(..))
 import Data.List as L
 import Data.Maybe (Maybe(..))
 import LinkedIn.DetachedNode (DetachedNode(..))
-import LinkedIn.UI.Strings.Parser (uiStringP)
 import LinkedIn.UI.Elements.Types (UIElement(..))
+import LinkedIn.UI.Strings.Parser (uiStringP)
 import Parsing (ParseError(..), initialPos, runParser)
 
 -- TODO : should certainly use another type than ParseError here
@@ -22,6 +22,8 @@ toUIElement (DetachedSvgElement {id, dataTestIcon, tag: "svg"}) = case id <|> da
   Nothing -> Left (ParseError "SVG element could not be identified" initialPos)
 toUIElement (DetachedSvgElement _) = Left (ParseError "SVG element could not be identified" initialPos)
 
+toUIElement (DetachedImg {src}) = Right (UIImage src)
+
 toUIElement (DetachedLiIcon i) = Right (UIIcon i)
 
 toUIElement (DetachedButton {content, role, classes}) =  map toButton $ runParser content uiStringP

+ 1 - 0
src/LinkedIn/UI/Elements/Types.purs

@@ -12,6 +12,7 @@ data UIElement =
   | UILink String UIString
   | UIButton {role :: Maybe String, label :: UIString, mainClass :: Maybe String}
   | UIIcon String
+  | UIImage String
 
 derive instance Generic UIElement _
 instance Show UIElement where

+ 66 - 1
test/JobsUnifiedTopCard.purs

@@ -31,7 +31,7 @@ type TestCase a = {
 jobsUnifiedTopCardSpec :: Spec Unit
 jobsUnifiedTopCardSpec = do
   describe "Jobs top card parsing" do
-    for_ [jobOfferPage_3786945580] runTest
+    for_ [jobOfferPage_3786945580, jobOfferPage_3797662873] runTest
 
 runTest ∷ ∀ m. Monad m ⇒ TestCase JobOfferPage → SpecT Aff Unit m Unit
 runTest {detached, filePath, id, output} = do
@@ -118,3 +118,68 @@ jobOfferPage_3786945580 = {
     title: "Data Engineer H/F - Secteur Energie"
   })
 }
+
+jobOfferPage_3797662873 ∷ TestCase JobOfferPage
+jobOfferPage_3797662873 = {
+  id: "3797662873",
+  filePath: "test/examples/job_offer_3797662873.html",
+  url: "https://www.linkedin.com/jobs/view/3797662873/",
+  detached: JobOfferPage (JobsUnifiedTopCardElement {
+    actions: (TopCardActionButton
+      (DetachedButton { classes: ("jobs-apply-button" : "artdeco-button" : "artdeco-button--3" : "artdeco-button--primary" : "ember-view" : Nil), content: "Candidature simplifiée", role: Nothing }))
+      : ((TopCardActionButton (DetachedButton {
+        classes: ("jobs-save-button" : "artdeco-button" : "artdeco-button--3" : "artdeco-button--secondary" : Nil),
+        content: "Enregistrer Enregistrer Data Engineer Confirmé(e)/Senior (CDI) chez CBTW",
+        role: Nothing
+      })) : Nil),
+    header: (DetachedElement {
+      classes: ("t-24" : "t-bold" : "job-details-jobs-unified-top-card__job-title" : Nil),
+      content: "Data Engineer Confirmé(e)/Senior (CDI)",
+      id: Nothing,
+      tag: "H1"
+    }),
+    insights: TopCardInsight {
+        content: (TopCardInsightContentSecondary {
+          primary: (DetachedElement { classes: Nil, content: "Hybride", id: Nothing, tag: "SPAN" }),
+          secondary: (NonEmptyList (NonEmpty (TopCardSecondaryInsightNested
+          (DetachedElement { classes: Nil, content: "Temps plein", id: Nothing, tag: "SPAN" })
+          ) ((TopCardSecondaryInsightPlain (
+            DetachedElement { classes: ("job-details-jobs-unified-top-card__job-insight-view-model-secondary" : Nil), content: "Confirmé", id: Nothing, tag: "SPAN" })) : Nil)))
+        }),
+        icon: (DetachedLiIcon "job")
+      } : TopCardInsight {
+        content: (TopCardInsightContentSingle (DetachedElement { classes: Nil, content: "1 001-5 000 employés · Développement de logiciels", id: Nothing, tag: "SPAN" })),
+        icon: (DetachedLiIcon "company")
+      } : TopCardInsight {
+        content: (TopCardInsightContentSingle (DetachedElement { classes: Nil, content: "Amina Berkane recrute à ce poste", id: Nothing, tag: "SPAN" })),
+        icon: (DetachedImg { classes: ("ivm-view-attr__img--centered" : "EntityPhoto-circle-1" : "evi-image" : "lazy-image" : "ember-view" : Nil), src: "job_offer_2_files/1706175960816.jpg" })
+      } : TopCardInsight {
+        content: (TopCardInsightContentButton (DetachedButton { classes: ("job-details-jobs-unified-top-card__job-insight-text-button" : Nil), content: "8 compétences sur 10 correspondent à votre profil, vous pourriez bien convenir pour ce poste", role: Nothing })),
+        icon: (DetachedSvgElement { dataTestIcon: (Just "checklist-medium"), id: Nothing, tag: "svg" })
+      } : TopCardInsight {
+        content: (TopCardInsightContentSingle (DetachedElement { classes: Nil, content: "Le délai d’évaluation des candidatures est généralement de 4 jours", id: Nothing, tag: "SPAN" })),
+        icon: (DetachedSvgElement { dataTestIcon: (Just "responsive-medium"), id: Nothing, tag: "svg" })
+      } : TopCardInsight {
+        content: (TopCardInsightContentSingle (DetachedElement { classes: Nil, content: "Découvrez comment vous vous positionnez par rapport à 39 candidats. Essai Premium pour 0 EUR", id: Nothing, tag: "SPAN" })),
+        icon: (DetachedSvgElement { dataTestIcon: (Just "lightbulb-medium"), id: Nothing, tag: "svg" })
+      } : Nil,
+    primaryDescription: (TopCardPrimaryDescription {
+      link: (DetachedA { content: "CBTW", href: "https://www.linkedin.com/company/collaboration-betters-the-world/life" }),
+      text: (DetachedText "· Paris et périphérie ·"),
+      tvmText: DetachedElement { classes: ("tvm__text" : "tvm__text--neutral" : Nil), content: "il y a 4 semaines", id: Nothing, tag: "SPAN" }
+        : (DetachedElement { classes: ("tvm__text" : "tvm__text--neutral" : Nil), content: "·", id: Nothing, tag: "SPAN" })
+        : (DetachedElement { classes: ("tvm__text" : "tvm__text--neutral" : Nil), content: "39 candidats", id: Nothing, tag: "SPAN" })
+        : Nil
+    })
+  }),
+  output: OutJobOffer (JobOffer {
+    companyDomain: Just "Développement de logiciels",
+    companyLink: "https://www.linkedin.com/company/collaboration-betters-the-world/life",
+    companyName: "CBTW",
+    companySize: Just "1 001-5 000 employés",
+    flexibility: Just JobFlexHybrid,
+    hasSimplifiedApplicationProcess: true,
+    location: (Just "Paris et périphérie ·"),
+    title: "Data Engineer Confirmé(e)/Senior (CDI)"
+  })
+}

File diff suppressed because it is too large
+ 3603 - 0
test/examples/job_offer_3797662873.html