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 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 |
-
-
-
-
-
-
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}
+
+
+
+`;
+}