diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9be66ac..3e2f302 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,10 @@ jobs: - name: Check project run: | deno task check:types + - name: Install Chromium + run: deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts + env: + PUPPETEER_PRODUCT: chrome - name: Run tests run: | deno task test diff --git a/deno.json b/deno.json index fce9cd2..11c487a 100644 --- a/deno.json +++ b/deno.json @@ -2,11 +2,11 @@ "tasks": { "build": "deno run --allow-read --allow-write --allow-net --allow-run --allow-env ./style/patch.ts && deno fmt", "check:types": "deno check **/*.ts", - "coverage": "rm -rf cov_profile && deno test --allow-read --coverage=cov_profile", + "coverage": "rm -rf cov_profile && deno test --allow-read --allow-env --allow-write --allow-run --allow-net --coverage=cov_profile", "dev": "deno run -A --unstable --watch --no-check ./example/main.ts", "ok": "deno fmt --check && deno lint && deno task check:types && deno task test", "report": "deno coverage cov_profile --html", - "test": "deno test --allow-read" + "test": "deno test --allow-read --allow-env --allow-write --allow-run --allow-net" }, "fmt": { "exclude": ["./test/fixtures/alerts.md"] diff --git a/test/fixtures/basic_md_table.html b/test/fixtures/basic_md_table.html deleted file mode 100644 index c4bf6ca..0000000 --- a/test/fixtures/basic_md_table.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Fruit NameQuantityUnit Cost per ItemSubtotal
Apple1$1.50$1.50
Pear2$2.00$4.00
Orange3$2.50$7.50
Grape60$0.05$3.00
Total$16.00
- -
- - diff --git a/test/server_test.ts b/test/server_test.ts new file mode 100644 index 0000000..17974af --- /dev/null +++ b/test/server_test.ts @@ -0,0 +1,93 @@ +import { assert } from "./test_deps.ts"; +import { render } from "../mod.ts"; +import { browserTest, setupHtmlWithCss } from "./test_utils.ts"; + +Deno.test("basic md table with dollar signs", async () => { + const markdown = `| Fruit Name | Quantity | Unit Cost per Item | Subtotal | + |------------|----------|--------------------|----------| + | Apple | 1 | $1.50 | $1.50 | + | Pear | 2 | $2.00 | $4.00 | + | Orange | 3 | $2.50 | $7.50 | + | Grape | 60 | $0.05 | $3.00 | + | Total | | | $16.00 |`; + + const body = render(markdown); + const html = setupHtmlWithCss(body); + + await browserTest(html, async (page, address) => { + await page.goto(`${address}`); + + await page.waitForSelector("table", { timeout: 1000 }); + const tableExists = await page.evaluate(() => { + const table = document.querySelector("table"); + return table !== null; + }); + assert(tableExists, "Table should be rendered"); + + const getCellText = (row: number, col: number) => { + return page.evaluate( + (row, col) => { + const cell = document.querySelector( + `table tr:nth-child(${row}) td:nth-child(${col})`, + ); + return cell ? cell.textContent?.trim() : null; + }, + row, + col, + ); + }; + + assert( + await getCellText(1, 1) === "Apple", + "First row, first column should be 'Apple'", + ); + assert( + await getCellText(2, 2) === "2", + "Second row, second column should be '2'", + ); + assert( + await getCellText(2, 3) === "$2.00", + "Second row, third column should be '$2.00'", + ); + assert( + await getCellText(5, 4) === "$16.00", + "Fifth row, fourth column should be '$16.00'", + ); + + const getComputedStyle = ( + selector: string, + property: keyof CSSStyleDeclaration, + ) => { + return page.evaluate( + (selector, property) => { + const element = document.querySelector(selector); + if (!element) { + return null; + } + const style = window.getComputedStyle(element); + return style[property]; + }, + selector, + property, + ); + }; + + const firstRowBgColor = await getComputedStyle( + "body > main > table > tbody > tr:nth-child(1)", + "backgroundColor", + ); + assert( + firstRowBgColor === "rgb(255, 255, 255)", + "Table background color should be white", + ); + + const secondRowBgColor = await getComputedStyle( + "body > main > table > tbody > tr:nth-child(2)", + "backgroundColor", + ); + assert( + secondRowBgColor === "rgb(246, 248, 250)", + "Table background color should be white", + ); + }); +}); diff --git a/test/test.ts b/test/test.ts index 41d8f97..9f33608 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,9 +1,5 @@ -import { - assertEquals, - assertStringIncludes, -} from "https://deno.land/std@0.211.0/assert/mod.ts"; -import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.43/deno-dom-wasm.ts"; -import { CSS, render, Renderer } from "../mod.ts"; +import { assertEquals, assertStringIncludes, DOMParser } from "./test_deps.ts"; +import { render, Renderer } from "../mod.ts"; Deno.test("Basic markdown", async () => { const markdown = await Deno.readTextFile("./test/fixtures/basic.md"); @@ -288,44 +284,6 @@ Deno.test("expect console warning from invalid math", () => { console.warn = originalWarn; }); -Deno.test("basic md table with dollar signs", () => { - const markdown = `| Fruit Name | Quantity | Unit Cost per Item | Subtotal | - |------------|----------|--------------------|----------| - | Apple | 1 | $1.50 | $1.50 | - | Pear | 2 | $2.00 | $4.00 | - | Orange | 3 | $2.50 | $7.50 | - | Grape | 60 | $0.05 | $3.00 | - | Total | | | $16.00 |`; - - const body = render(markdown); - const html = ` - - - - - - - - -
- ${body} -
- - -`; - // uncomment to update the fixture when the css changes - // Deno.writeTextFileSync("./test/fixtures/basic_md_table.html", html); - - const expected = Deno.readTextFileSync("./test/fixtures/basic_md_table.html"); - assertEquals(html, expected); -}); - Deno.test("footnotes", () => { const markdown = Deno.readTextFileSync("./test/fixtures/footnote.md"); const expected = Deno.readTextFileSync("./test/fixtures/footnote.html"); diff --git a/test/test_deps.ts b/test/test_deps.ts new file mode 100644 index 0000000..45e470c --- /dev/null +++ b/test/test_deps.ts @@ -0,0 +1,11 @@ +export { + assert, + assertEquals, + assertStringIncludes, +} from "https://deno.land/std@0.211.0/assert/mod.ts"; +export { DOMParser } from "https://deno.land/x/deno_dom@v0.1.43/deno-dom-wasm.ts"; +export { + Browser, + default as puppeteer, + Page, +} from "https://deno.land/x/puppeteer@16.2.0/mod.ts"; diff --git a/test/test_utils.ts b/test/test_utils.ts new file mode 100644 index 0000000..ca0737e --- /dev/null +++ b/test/test_utils.ts @@ -0,0 +1,64 @@ +import { Page, puppeteer } from "./test_deps.ts"; +import { CSS } from "../mod.ts"; + +export async function browserTest( + htmlContent: string, + fn: (page: Page, address: string) => Promise, + port = 8000, +) { + const { serverProcess, address } = await startServer(htmlContent, port); + + try { + const browser = await puppeteer.launch({ + args: ["--no-sandbox"], + headless: true, + }); + + try { + const page = await browser.newPage(); + await fn(page, address); + } finally { + await browser.close(); + } + } finally { + serverProcess.shutdown(); + } +} + +function startServer(htmlContent: string, port: number) { + const serverProcess = Deno.serve({ port }, (_req) => { + return new Response(htmlContent, { + headers: { "Content-Type": "text/html" }, + }); + }); + + const hostname = "localhost"; + const address = `http://${hostname}:${port}`; + + console.log(`Server running at ${address}`); + + return { serverProcess, address }; +} + +export function setupHtmlWithCss(bodyContent: string): string { + return ` + + + + + + + +
+ ${bodyContent} +
+ + +`; +}