diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..7645e87 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,29 @@ +name: Playwright Tests +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] +jobs: + test: + env: + PB_ADMIN_BASIC: ${{ secrets.PB_ADMIN_BASIC }} + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 60355bf..0127989 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ src/pb.d.ts /pocketbase-data .firebase +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index 162d878..f97e3c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,13 @@ "dependencies": { "@types/eventsource": "^1.1.15", "@types/node": "^22.9.0", + "dotenv": "^16.4.5", "eventsource": "^2.0.2", "pocketbase": "^0.21.5", "vite-plugin-node-polyfills": "^0.22.0" }, "devDependencies": { + "@playwright/test": "^1.48.2", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^5.2.7", "@sveltejs/kit": "^2.5.27", @@ -755,6 +757,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.28", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", @@ -2445,6 +2463,18 @@ "url": "https://bevry.me/fund" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4231,6 +4261,53 @@ "node": ">=10" } }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/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/pocketbase": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.21.5.tgz", diff --git a/package.json b/package.json index c3237f5..8be145d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "check:format": "prettier --check ." }, "devDependencies": { + "@playwright/test": "^1.48.2", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^5.2.7", "@sveltejs/kit": "^2.5.27", @@ -39,6 +40,7 @@ "dependencies": { "@types/eventsource": "^1.1.15", "@types/node": "^22.9.0", + "dotenv": "^16.4.5", "eventsource": "^2.0.2", "pocketbase": "^0.21.5", "vite-plugin-node-polyfills": "^0.22.0" diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..7aa1ca3 --- /dev/null +++ b/playwright.config.ts @@ -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(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./src/test", + /* 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://localhost:5173/", + + /* 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 dev", + url: "http://localhost:5173/", + reuseExistingServer: !process.env.CI + } +}); diff --git a/src/lib/components/AuthButton.svelte b/src/lib/components/AuthButton.svelte index 6bdca35..93cb156 100644 --- a/src/lib/components/AuthButton.svelte +++ b/src/lib/components/AuthButton.svelte @@ -47,6 +47,15 @@ goHome(); } + async function loginWithBasicAuth() { + const username = prompt("Enter your username:"); + const password = prompt("Enter your password:"); + const authData = await pb.collection("users").authWithPassword(username!, password!); + console.log(authData); + document.cookie = pb.authStore.exportToCookie({ httpOnly: false }); + goHome(); + } + function logout() { pb.authStore.clear(); document.cookie = pb.authStore.exportToCookie({ httpOnly: false }); @@ -59,7 +68,7 @@ >{#if children}{@render children()}{:else}Logg ut{/if} {:else} - {/if} diff --git a/src/test/send-order.spec.ts b/src/test/send-order.spec.ts new file mode 100644 index 0000000..d74f084 --- /dev/null +++ b/src/test/send-order.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from "@playwright/test"; + +test("test", async ({ page, browserName }) => { + if (browserName === "webkit") { + test.skip(); + } + + const [username, password] = process.env.PB_ADMIN_BASIC!.split(":"); + page.on("dialog", async (dialog) => { + if (dialog.type() === "prompt") { + if (dialog.message().includes("Enter your username:")) { + await dialog.accept(username); + } else if (dialog.message().includes("Enter your password:")) { + await dialog.accept(password); + } else { + await dialog.dismiss(); + } + } + }); + + // log in + await page.goto("/"); + await page.waitForLoadState("networkidle"); + await page.getByRole("button", { name: "Logg inn", exact: true }).click({ + button: "right" + }); + + await page.getByRole("link", { name: "Admin" }).click(); + await page.getByRole("link", { name: "Bestillingsdisk" }).click(); + + // add mocca + await page.getByText("Mocca 10,-").click(); + await page.getByRole("button", { name: "+" }).click(); + await expect(page.getByRole("cell", { name: "Mocca" })).toBeVisible(); + await expect(page.locator("tfoot")).toContainText("10,-"); + + // add hot chocolate + await page.getByText("Varm sjokolade").click(); + await page.getByRole("button", { name: "+" }).click(); + await expect(page.getByRole("cell", { name: "Varm sjokolade" })).toBeVisible(); + await expect(page.locator("tfoot")).toContainText("20,-"); + + // finish order + await page.getByRole("button", { name: "Ferdig" }).click(); + await expect(page.getByRole("cell", { name: "Ingenting" })).toBeVisible(); + + // logout + await page.getByText("Tilbake").click(); + await page.getByRole("button", { name: "Logg ut" }).click(); +});