diff --git a/__tests__/LandingPage_.test.res b/__tests__/LandingPage_.test.res index 4bf3b0338..d84bb2d45 100644 --- a/__tests__/LandingPage_.test.res +++ b/__tests__/LandingPage_.test.res @@ -1,6 +1,9 @@ open ReactRouter open Vitest +@module("vitest") +external testWithTimeout: (string, unit => promise, int) => unit = "test" + let expectedExample = `module Button = { @react.component let make = (~count) => { @@ -15,6 +18,126 @@ let expectedExample = `module Button = { } }` +let snapshotSection = async (~width, ~height, ~sectionTestId, ~screenshotName) => { + await viewport(width, height) + + let screen = await render( + + + , + ) + + let sourceSection = switch document->WebAPI.Document.querySelector( + `[data-testid="${sectionTestId}"]`, + ) { + | Value(section) => section + | Null => failwith(`expected to find section ${sectionTestId}`) + } + + let sandboxTestId = `${sectionTestId}-snapshot` + let snapshotHtml = sourceSection.outerHTML + await screen->unmount + + let snapshotScreen = await render( +
Int.toString}px`, margin: "0"}} + dangerouslySetInnerHTML={"__html": snapshotHtml} + />, + ) + + let snapshotTarget = await snapshotScreen->getByTestId(sandboxTestId) + await element(snapshotTarget)->toBeVisible + await element(snapshotTarget)->toMatchScreenshot(screenshotName) + await snapshotScreen->unmount +} + +let snapshotResponsive = async (~sectionTestId, ~screenshotBase) => { + await snapshotSection( + ~width=1440, + ~height=1800, + ~sectionTestId, + ~screenshotName={`${screenshotBase}-desktop`}, + ) + + await snapshotSection( + ~width=900, + ~height=1800, + ~sectionTestId, + ~screenshotName={`${screenshotBase}-tablet`}, + ) + + await snapshotSection( + ~width=600, + ~height=1800, + ~sectionTestId, + ~screenshotName={`${screenshotBase}-mobile`}, + ) +} + +testWithTimeout( + "landing intro snapshots", + async () => { + await snapshotResponsive(~sectionTestId="landing-intro", ~screenshotBase="landing-page-intro") + }, + 30000, +) + +testWithTimeout( + "landing playground hero snapshots", + async () => { + await snapshotResponsive( + ~sectionTestId="landing-playground-hero", + ~screenshotBase="landing-page-playground-hero", + ) + }, + 30000, +) + +testWithTimeout( + "landing quick install snapshots", + async () => { + await snapshotResponsive( + ~sectionTestId="landing-quick-install", + ~screenshotBase="landing-page-quick-install", + ) + }, + 30000, +) + +testWithTimeout( + "landing other selling points snapshots", + async () => { + await snapshotResponsive( + ~sectionTestId="landing-other-selling-points", + ~screenshotBase="landing-page-other-selling-points", + ) + }, + 30000, +) + +testWithTimeout( + "landing trusted by snapshots", + async () => { + await snapshotResponsive( + ~sectionTestId="landing-trusted-by", + ~screenshotBase="landing-page-trusted-by", + ) + }, + 30000, +) + +testWithTimeout( + "landing curated resources snapshots", + async () => { + await snapshotResponsive( + ~sectionTestId="landing-curated-resources", + ~screenshotBase="landing-page-curated-resources", + ) + }, + 30000, +) + test( "landing page playground link uses compressed code that the playground can decode", async () => { @@ -53,3 +176,94 @@ test( expect(decodedCode->Option.getOrThrow)->toBe(expectedExample) }, ) + +test("landing page playground hero renders highlighted code tokens", async () => { + let screen = await render( + + + , + ) + + let _ = await screen->getByText("Write in ReScript") + + let rescriptCodeBlock = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero'] code.lang-res", + ) { + | Value(codeBlock) => codeBlock + | Null => failwith("expected landing playground hero to render the ReScript code block") + } + + let javascriptCodeBlock = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero'] code.lang-js", + ) { + | Value(codeBlock) => codeBlock + | Null => failwith("expected landing playground hero to render the JavaScript code block") + } + + expect(rescriptCodeBlock.innerHTML->String.includes("toBe(true) + expect(javascriptCodeBlock.innerHTML->String.includes("toBe(true) +}) + +test( + "landing page playground hero keeps highlight styling in the sandboxed snapshot copy", + async () => { + await viewport(1440, 1800) + + let screen = await render( + + + , + ) + + let sourceSection = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero']", + ) { + | Value(section) => section + | Null => failwith("expected to find the landing playground hero section") + } + + let sandboxTestId = "landing-playground-hero-sandbox" + let snapshotHtml = sourceSection.outerHTML + await screen->unmount + + let snapshotScreen = await render( +
, + ) + + let _ = await snapshotScreen->getByTestId(sandboxTestId) + + let sandbox = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero-sandbox']", + ) { + | Value(sandbox) => sandbox + | Null => failwith("expected to find the sandboxed landing playground hero") + } + + let sandboxRect: WebAPI.DOMAPI.domRect = sandbox->WebAPI.Element.getBoundingClientRect + + let rescriptCodeBlock = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero-sandbox'] code.lang-res", + ) { + | Value(codeBlock) => codeBlock + | Null => + failwith("expected sandboxed landing playground hero to render the ReScript code block") + } + + let javascriptCodeBlock = switch document->WebAPI.Document.querySelector( + "[data-testid='landing-playground-hero-sandbox'] code.lang-js", + ) { + | Value(codeBlock) => codeBlock + | Null => + failwith("expected sandboxed landing playground hero to render the JavaScript code block") + } + + expect(sandboxRect.width > 1000.0)->toBe(true) + expect(rescriptCodeBlock.innerHTML->String.includes("toBe(true) + expect(javascriptCodeBlock.innerHTML->String.includes("toBe(true) + await snapshotScreen->unmount + }, +) diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-desktop-chromium-linux.png new file mode 100644 index 000000000..611d7bd6a Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-mobile-chromium-linux.png new file mode 100644 index 000000000..d51a5c4ed Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-tablet-chromium-linux.png new file mode 100644 index 000000000..bef337f90 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-curated-resources-tablet-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-desktop-chromium-linux.png new file mode 100644 index 000000000..6354475b9 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-mobile-chromium-linux.png new file mode 100644 index 000000000..24612c8b1 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-tablet-chromium-linux.png new file mode 100644 index 000000000..8598a2997 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-intro-tablet-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-desktop-chromium-linux.png new file mode 100644 index 000000000..61d5acd78 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-mobile-chromium-linux.png new file mode 100644 index 000000000..f94537cbd Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-tablet-chromium-linux.png new file mode 100644 index 000000000..ac7aaaeee Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-other-selling-points-tablet-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-desktop-chromium-linux.png new file mode 100644 index 000000000..6759a1d9a Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-mobile-chromium-linux.png new file mode 100644 index 000000000..8dd751a7f Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-tablet-chromium-linux.png new file mode 100644 index 000000000..3e7f10b6d Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-playground-hero-tablet-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-desktop-chromium-linux.png new file mode 100644 index 000000000..e37fdf1d0 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-mobile-chromium-linux.png new file mode 100644 index 000000000..f687ccfd9 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-tablet-chromium-linux.png new file mode 100644 index 000000000..371a4072b Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-quick-install-tablet-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-desktop-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-desktop-chromium-linux.png new file mode 100644 index 000000000..58e5aacac Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-desktop-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-mobile-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-mobile-chromium-linux.png new file mode 100644 index 000000000..cb0a44319 Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-mobile-chromium-linux.png differ diff --git a/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-tablet-chromium-linux.png b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-tablet-chromium-linux.png new file mode 100644 index 000000000..574ed100c Binary files /dev/null and b/__tests__/__screenshots__/LandingPage_.test.jsx/landing-page-trusted-by-tablet-chromium-linux.png differ diff --git a/app/routes/LandingPage.res b/app/routes/LandingPage.res index feda1e7c2..f83386fab 100644 --- a/app/routes/LandingPage.res +++ b/app/routes/LandingPage.res @@ -1,7 +1,7 @@ module Intro = { @react.component let make = () => { -
+
// We only need this font on the homepage, so we load it here instead of globally to save some bandwidth for users who navigate to other pages directly -
- - - -
+ // Keep the CTA link block-level so the moved top/bottom margins still affect layout after removing its wrapper. + + +
} @@ -82,61 +83,56 @@ export { let (example, _setExample) = React.useState(_ => examples->Array.getUnsafe(0)) //Playground Section & Background -
-
-
- // Playground widget -
- //Left Side (ReScript) -
-
- {React.string("Write in ReScript")} -
-
-                {HighlightJs.renderHLJS(~darkmode=true, ~code=example.res, ~lang="res", ())}
-              
-
- //Right Side (JavaScript) -
-
- {React.string("Compile to JavaScript")} -
-
-                {HighlightJs.renderHLJS(~darkmode=true, ~code=example.js, ~lang="js", ())}
-              
+
+
+ // `mx-auto` preserves the removed centering wrapper's max-width behavior for the playground frame. + // Playground widget +
+ //Left Side (ReScript) +
+
+ {React.string("Write in ReScript")}
+
+              {HighlightJs.renderHLJS(~darkmode=true, ~code=example.res, ~lang="res", ())}
+            
- - /* ---Link to Playground--- */ -
- +
- {React.string("Edit this example in Playground")} - -
- // -
- - -
-
- - + {React.string("Compile to JavaScript")} +
+
+              {HighlightJs.renderHLJS(~darkmode=true, ~code=example.js, ~lang="js", ())}
+            
+ + /* ---Link to Playground--- */ + + {React.string("Edit this example in Playground")} + + // +
+ + +
+
+ + +
} @@ -249,38 +245,37 @@ module QuickInstall = {
} @react.component - let make = () => { -
+ let make = (~className: string="") => { +

{React.string("Quick Install")}

{React.string( "You can quickly add ReScript to your existing JavaScript codebase via npm / yarn:", )}
-
{copyBox("npm install rescript")}
+ {copyBox("npm install rescript")}
{React.string("Or generate a new project from the official template with npx:")}
-
{copyBox("npx create-rescript-app")}
+ {copyBox("npx create-rescript-app")}
} } @react.component let make = () => { -
+
//---Textblock on the left side--- -
-

- - {React.string(`Leverage the full power`)} - - {React.string(` of JavaScript in a robustly typed language without the fear of \`any\` types.`)} -

-
+ // Keep the width clamp on the paragraph itself now that the wrapper is gone. +

+ + {React.string(`Leverage the full power`)} + + {React.string(` of JavaScript in a robustly typed language without the fear of \`any\` types.`)} +

//spacing between columns

{React.string(`ReScript is used to ship and maintain mission-critical products with good UI and UX.`)}

-
- -
+ // The alignment classes move onto the instructions root so the column still hugs the lower edge without an extra wrapper. +
@@ -335,9 +329,7 @@ module MainUSP = {
{React.string(caption)}

title

-
-
paragraph
-
+
paragraph
//image (right)
@@ -450,6 +442,7 @@ module OtherSellingPoints = { @react.component let make = () => {
@@ -473,13 +466,12 @@ module OtherSellingPoints = { Join the ReScript community — A group of companies and individuals who deeply care about simplicity, speed and practicality.`)}

- + // The forum link becomes inline-block so the moved top margin still participates in the card flow. + + +
// 2 small items // Item 2 @@ -523,7 +515,7 @@ module OtherSellingPoints = { module TrustedBy = { @react.component let make = () => { -
+

{React.string("Trusted by our users")}

@@ -531,17 +523,14 @@ module TrustedBy = { className="flex flex-wrap mx-4 gap-8 justify-center items-center max-w-xl lg:mx-auto mt-16 mb-16" > {OurUsers.companies - ->Array.map(company => { - let (companyKey, renderedCompany) = switch company { - | Logo({name, path, url}) => ( - name, - - - , - ) + ->Array.map(company => + switch company { + | Logo({name, path, url}) => + + + } -
renderedCompany
- }) + ) ->React.array}
-
- -
+ // The grid image now owns the alignment classes directly because the wrapper only clipped a single child. +
} } @@ -595,8 +583,8 @@ module CuratedResources = { { imgSrc: "/nextjs_starter_logo.svg", title: <> -
{React.string("ReScript & ")}
-
{React.string("NextJS")}
+ {React.string("ReScript & ")} + {React.string("NextJS")} , descr: "Get started with our NextJS starter template.", href: "https://github.com/rescript-lang/create-rescript-app/blob/master/templates/rescript-template-nextjs/README.md", @@ -604,8 +592,8 @@ module CuratedResources = { { imgSrc: "/vitejs_starter_logo.svg", title: <> -
{React.string("ReScript & ")}
-
{React.string("ViteJS")}
+ {React.string("ReScript & ")} + {React.string("ViteJS")} , descr: "Get started with ViteJS and ReScript.", href: "https://github.com/rescript-lang/create-rescript-app/blob/master/templates/rescript-template-vite/README.md", @@ -613,8 +601,10 @@ module CuratedResources = { { imgSrc: "/nodejs_starter_logo.svg", title: <> -
{React.string("ReScript & ")}
-
{React.string("NodeJS")}
+ {React.string("ReScript & ")} + + {React.string("NodeJS")} + , descr: "Get started with ReScript targeting the Node platform.", href: "/", @@ -623,7 +613,7 @@ module CuratedResources = { @react.component let make = () => { -
+
//headline container
{ confidence as your codebase grows.`} keywords=["ReScript", "rescriptlang", "JavaScript", "JS", "TypeScript"] /> -
-
-
- // Enable this when v13 is released - // - // {React.string("ReScript 12 is out! Read the ")} - // - // {React.string("announcement blog post")} - // - // {React.string(".")} - // -
-
-
-
-
- -
- - - - - - -
-
-
+ // Keep the inherited typography and stacking context on the absolute shell while removing the inert wrapper chain around the page content. +
+ // Enable this when v13 is released + // + // {React.string("ReScript 12 is out! Read the ")} + // + // {React.string("announcement blog post")} + // + // {React.string(".")} + // +
+
+
+
-
-
+ + + + + + +
+
} diff --git a/src/bindings/Vitest.res b/src/bindings/Vitest.res index b8f20fef8..f80d7f651 100644 --- a/src/bindings/Vitest.res +++ b/src/bindings/Vitest.res @@ -27,6 +27,9 @@ external viewport: (int, int) => promise = "viewport" @module("vitest-browser-react") external render: Jsx.element => promise = "render" +@send +external unmount: element => promise = "unmount" + @module("vitest") @scope("expect") external element: 'a => element = "element" diff --git a/vitest.setup.mjs b/vitest.setup.mjs index 3ba70e746..acb31a714 100644 --- a/vitest.setup.mjs +++ b/vitest.setup.mjs @@ -1,3 +1,29 @@ import "./styles/main.css"; +import "./styles/_hljs.css"; import "./styles/utils.css"; import "./styles/test-overrides.css"; + +import hljs from "highlight.js/lib/core"; +import bash from "highlight.js/lib/languages/bash"; +import css from "highlight.js/lib/languages/css"; +import diff from "highlight.js/lib/languages/diff"; +import javascript from "highlight.js/lib/languages/javascript"; +import typescript from "highlight.js/lib/languages/typescript"; +import json from "highlight.js/lib/languages/json"; +import text from "highlight.js/lib/languages/plaintext"; +import html from "highlight.js/lib/languages/xml"; +import toml from "highlight.js/lib/languages/ini"; +import rescript from "highlightjs-rescript"; + +hljs.registerLanguage("rescript", rescript); +hljs.registerLanguage("javascript", javascript); +hljs.registerLanguage("css", css); +hljs.registerLanguage("ts", typescript); +hljs.registerLanguage("sh", bash); +hljs.registerLanguage("bash", bash); +hljs.registerLanguage("toml", toml); +hljs.registerLanguage("json", json); +hljs.registerLanguage("text", text); +hljs.registerLanguage("html", html); +hljs.registerLanguage("diff", diff); +hljs.registerLanguage("typescript", typescript);