chore: import upstream snapshot with attribution
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:31:57 +08:00
commit d718c5a372
986 changed files with 74597 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
+386
View File
@@ -0,0 +1,386 @@
import { expect, type Page, test } from "@playwright/test";
const BASE = "http://localhost:8080/";
test("Open documentation", async ({ page }) => {
await page.goto(BASE);
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/SQLPage.*/);
// open the submenu
await page.getByText("Documentation", { exact: true }).first().click();
const components = ["form", "map", "chart", "button"];
for (const component of components) {
await expect(
page.getByRole("link", { name: component }).first(),
).toBeVisible();
}
});
test("chart", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=chart#component`);
await expect(page.getByText("Loading...")).not.toBeVisible();
await expect(page.locator(".apexcharts-canvas").first()).toBeVisible();
});
test("chart supports hiding legend", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=chart#component`);
const expensesChart = page.locator(".card", {
has: page.getByRole("heading", { name: "Expenses" }),
});
await expect(expensesChart.locator(".apexcharts-canvas")).toBeVisible();
await expect(expensesChart.locator(".apexcharts-legend")).toBeHidden();
});
test("map", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=map#component`);
await expect(page.getByText("Loading...")).not.toBeVisible();
await expect(page.locator(".leaflet-marker-icon").first()).toBeVisible();
});
test("form example", async ({ page }) => {
await page.goto(`${BASE}/examples/multistep-form`);
// Single selection matching the value or label
await page.getByLabel("From").selectOption("Paris");
await page.getByText("Next").click();
await page.getByLabel(/\bTo\b/).selectOption("Mexico");
await page.getByText("Next").click();
await page.getByLabel("Number of Adults").fill("1");
await page.getByText("Next").click();
await page.getByLabel("Passenger 1 (adult)").fill("John Doe");
await page.getByText("Book the flight").click();
await expect(page.getByText("John Doe").first()).toBeVisible();
});
test("File upload", async ({ page }) => {
await page.goto(`${BASE}/your-first-sql-website`);
await page.getByRole("button", { name: "Examples", exact: true }).click();
await page.getByText("File uploads").click();
const my_svg = '<svg><text y="20">Hello World</text></svg>';
// @ts-ignore
const buffer = Buffer.from(my_svg);
await page.getByLabel("Picture").setInputFiles({
name: "small.svg",
mimeType: "image/svg+xml",
buffer,
});
await page.getByRole("button", { name: "Upload picture" }).click();
await expect(
page.locator("img[src^=data]").first().getAttribute("src"),
).resolves.toBe(`data:image/svg+xml;base64,${buffer.toString("base64")}`);
});
test("Authentication example", async ({ page }) => {
await page.goto(`${BASE}/examples/authentication/login.sql`);
await expect(page.locator("h1", { hasText: "Authentication" })).toBeVisible();
const usernameInput = page.getByLabel("Username");
const passwordInput = page.getByLabel("Password");
const loginButton = page.getByRole("button", { name: "Log in" });
await expect(usernameInput).toBeVisible();
await expect(passwordInput).toBeVisible();
await expect(loginButton).toBeVisible();
await usernameInput.fill("admin");
await passwordInput.fill("admin");
await loginButton.click();
await expect(page.getByText("You are logged in as admin")).toBeVisible();
});
test("table filtering", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".card", {
has: page.getByRole("cell", { name: "Chart", exact: true }),
});
const searchInput = tableSection.getByPlaceholder("Search…");
await searchInput.fill("chart");
const chartCell = tableSection.getByRole("cell", { name: "Chart" });
await expect(chartCell).toBeVisible();
await expect(chartCell).toHaveClass(/\b_col_name\b/);
await expect(chartCell).toHaveCSS("vertical-align", "middle");
await expect(
tableSection.getByRole("cell", { name: "Table" }),
).not.toBeVisible();
});
test("table sorting", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".table-responsive", {
has: page.getByRole("cell", { name: "31456" }),
});
// Test numeric sorting on id column
await tableSection.getByRole("button", { name: "id" }).click();
let ids = await tableSection.locator("td.id").allInnerTexts();
let numericIds = ids.map((id) => Number.parseInt(id));
const sortedIds = [...numericIds].sort((a, b) => a - b);
expect(numericIds).toEqual(sortedIds);
// Test reverse sorting
await tableSection.getByRole("button", { name: "id" }).click();
ids = await tableSection.locator("td.id").allInnerTexts();
numericIds = ids.map((id) => Number.parseInt(id));
const reverseSortedIds = [...numericIds].sort((a, b) => b - a);
expect(numericIds).toEqual(reverseSortedIds);
// Test amount in stock column sorting
await tableSection.getByRole("button", { name: "Amount in stock" }).click();
const amounts = await tableSection.locator("td.Amount").allInnerTexts();
const numericAmounts = amounts.map((amount) =>
Number.parseInt(amount.replace(/[^0-9]/g, "")),
);
const sortedAmounts = [...numericAmounts].sort((a, b) => a - b);
expect(numericAmounts).toEqual(sortedAmounts);
});
async function checkNoConsoleErrors(page: Page, component: string) {
const errors: string[] = [];
page.on("console", (msg) => {
if (msg.type() === "error") {
errors.push(msg.text());
}
});
await page.goto(`${BASE}/documentation.sql?component=${component}`);
await page.waitForLoadState();
expect(errors).toHaveLength(0);
}
test("no console errors on table page", async ({ page }) => {
await checkNoConsoleErrors(page, "table");
});
test("no console errors on chart page", async ({ page }) => {
await checkNoConsoleErrors(page, "chart");
});
test("no console errors on map page", async ({ page }) => {
await checkNoConsoleErrors(page, "map");
});
test("no console errors on card page", async ({ page }) => {
await checkNoConsoleErrors(page, "card");
});
test("CSP issues unique nonces per request", async ({ page }) => {
const csp1 = await (await page.goto(BASE)).headerValue(
"content-security-policy",
);
const csp2 = await (await page.reload()).headerValue(
"content-security-policy",
);
expect(csp1, `check if ${csp1} != ${csp2}`).not.toEqual(csp2);
});
test("form component documentation", async ({ page }) => {
await page.goto(`${BASE}/component.sql?component=form`);
// Find the form that contains radio buttons for component selection
const componentForm = page.locator("form", {
has: page.getByRole("radio", { name: "Chart" }),
});
// the form should be visible
await expect(componentForm).toBeVisible();
// Check that "form" is the first and default selected option
const mapRadio = componentForm.getByRole("radio", { name: "Map" });
await expect(mapRadio).toHaveValue("map");
await expect(mapRadio).toBeChecked();
// Select "Chart" option and submit
await componentForm.getByLabel("Chart").click({ force: true });
await componentForm.getByRole("button", { name: "Submit" }).click();
// Verify we're on the chart documentation page
await expect(
page.getByRole("heading", { name: /chart/i, level: 1 }),
).toBeVisible();
});
test("form select combines initial options with remote search results", async ({
page,
}) => {
await page.goto(`${BASE}/component.sql?component=form`);
const select = page
.locator(
'select[data-options_source="examples/from_component_options_source.sql"]',
)
.first();
await expect(select).toBeAttached();
await page.waitForFunction(
() =>
!!document.querySelector<HTMLSelectElement>(
'select[data-options_source="examples/from_component_options_source.sql"]',
)?.tomselect,
);
const initialState = await select.evaluate((element) => {
const tomselect = element.tomselect;
return {
value: tomselect.getValue(),
labels: Object.fromEntries(
Object.entries(tomselect.options).map(([value, option]) => [
value,
option.label,
]),
),
};
});
expect(initialState).toEqual({
value: "form",
labels: { form: "Form" },
});
await select.evaluate((element) => element.tomselect.focus());
await page.keyboard.type("form");
await page.waitForResponse((response) =>
response
.url()
.includes("examples/from_component_options_source.sql?search=form"),
);
await expect
.poll(async () =>
select.evaluate((element) => ({
value: element.tomselect.getValue(),
formLabel: element.tomselect.options.form?.label,
})),
)
.toEqual({
value: "form",
formLabel: "form",
});
await select.evaluate((element) => element.tomselect.setTextboxValue(""));
await page.keyboard.type("map");
await page.waitForResponse((response) =>
response
.url()
.includes("examples/from_component_options_source.sql?search=map"),
);
await expect
.poll(async () =>
select.evaluate((element) => ({
value: element.tomselect.getValue(),
formLabel: element.tomselect.options.form?.label,
mapLabel: element.tomselect.options.map?.label,
})),
)
.toEqual({
value: "form",
formLabel: "form",
mapLabel: "map",
});
});
test("modal", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=modal#component`);
// get the button that opens the modal
const openButton = page.getByRole("button", { name: "Open a simple modal" });
await openButton.click();
const modal = page.getByRole("dialog", { label: "A modal box" });
await expect(modal).toBeVisible();
// close the modal
await page.keyboard.press("Escape");
await expect(modal).not.toBeVisible();
await openButton.click();
await expect(modal).toBeVisible();
await modal.getByRole("button", { label: "Close" }).first().click();
await expect(modal).not.toBeVisible();
});
test("table action buttons - edit_url and delete_url", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".table-responsive", {
has: page.getByRole("cell", { name: "PharmaCo" }),
});
const editButton = tableSection.getByTitle("Edit").first();
await expect(editButton).toBeVisible();
await expect(editButton).toHaveAttribute("href", /action=edit&update_id=\d+/);
const deleteButton = tableSection.getByTitle("Delete").first();
await expect(deleteButton).toBeVisible();
await expect(deleteButton).toHaveAttribute(
"href",
/action=delete&delete_id=\d+/,
);
});
test("table action buttons - custom_actions", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".table-responsive", {
has: page.getByRole("cell", { name: "PharmaCo" }),
});
const historyButton = tableSection
.getByTitle("View Standard History")
.first();
await expect(historyButton).toBeVisible();
await expect(historyButton).toHaveAttribute(
"href",
/action=history&standard_id=\d+/,
);
});
test("table action buttons - _sqlpage_actions", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".table-responsive", {
has: page.getByRole("cell", { name: "PharmaCo" }),
});
const pdfButtons = tableSection.getByTitle("View Presentation");
await expect(pdfButtons.first()).toBeVisible();
await expect(pdfButtons).toHaveCount(3);
const firstPdfButton = pdfButtons.first();
await expect(firstPdfButton).toHaveAttribute(
"href",
"https://sql-page.com/pgconf/2024-sqlpage-badass.pdf",
);
const setInUseButton = tableSection.getByTitle("Set In Use");
await expect(setInUseButton).toBeVisible();
await expect(setInUseButton).toHaveAttribute(
"href",
/action=set_in_use&standard_id=32/,
);
const retireButton = tableSection.getByTitle("Retire Standard");
await expect(retireButton).toBeVisible();
await expect(retireButton).toHaveAttribute(
"href",
/action=retire&standard_id=33/,
);
});
test("table action buttons - disabled action", async ({ page }) => {
await page.goto(`${BASE}/documentation.sql?component=table`);
const tableSection = page.locator(".table-responsive", {
has: page.getByRole("cell", { name: "PharmaCo" }),
});
const viewPresentationButtons = tableSection.getByTitle("View Presentation");
await expect(viewPresentationButtons).toHaveCount(3);
const actionColumnButtons = tableSection.locator(
"td._col_Action a[data-action='Action']",
);
await expect(actionColumnButtons).toHaveCount(3);
const emptyActionButton = actionColumnButtons.last();
await expect(emptyActionButton).toHaveAttribute("href", "null");
await expect(emptyActionButton).toHaveAttribute("title", "Action");
});
+97
View File
@@ -0,0 +1,97 @@
{
"name": "end-to-end",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "end-to-end",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.60.0",
"@types/node": "^25.9.1"
}
},
"node_modules/@playwright/test": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
"integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@types/node": {
"version": "25.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": ">=7.24.0 <7.24.7"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/undici-types": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"dev": true,
"license": "MIT"
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "end-to-end",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "playwright test --reporter=line",
"gui": "playwright test --ui"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.60.0",
"@types/node": "^25.9.1"
}
}
+78
View File
@@ -0,0 +1,78 @@
import { defineConfig, devices } from "@playwright/test";
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./.",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});