diff options
author | 2025-06-06 14:18:07 -0700 | |
---|---|---|
committer | 2025-06-06 14:18:07 -0700 | |
commit | 108f26a3874e5f8649bf4d128aa07c9b35aa6a10 (patch) | |
tree | d7f1e145ecca9d6e5e450bdf38353edd93aefe3f | |
parent | 774b221be12316230019b90fb597b4590ec37a01 (diff) | |
download | anshulg-com-108f26a3874e5f8649bf4d128aa07c9b35aa6a10.tar.gz anshulg-com-108f26a3874e5f8649bf4d128aa07c9b35aa6a10.tar.zst anshulg-com-108f26a3874e5f8649bf4d128aa07c9b35aa6a10.zip |
Add playwright tests
Diffstat (limited to '')
-rw-r--r-- | .github/workflows/playwright.yml | 25 | ||||
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | bun.lock | 18 | ||||
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | playwright.config.ts | 79 | ||||
-rw-r--r-- | src/components/server/Uptime.astro | 43 | ||||
-rw-r--r-- | tests/index.spec.ts | 48 |
7 files changed, 203 insertions, 20 deletions
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..f74fdd3 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,25 @@ +name: Playwright Tests +on: + push: + branches: [main] + pull_request: + branches: [main] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Install Playwright Browsers + run: bunx playwright install --with-deps + - name: Run Playwright tests + run: bunx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 @@ -25,3 +25,9 @@ pnpm-debug.log* # ESLint cache .eslintcache + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ @@ -12,6 +12,8 @@ "tailwindcss": "^4.1.8", }, "devDependencies": { + "@playwright/test": "^1.52.0", + "@types/node": "^22.15.30", "@typescript-eslint/parser": "8.33.1", "eslint": "9.28.0", "eslint-plugin-astro": "1.3.1", @@ -188,6 +190,8 @@ "@pkgr/core": ["@pkgr/core@0.2.5", "", {}, "sha512-YRx7tFgLkrpFkDAzVSV5sUJydmf2ZDrW+O3IbQ1JyeMW7B0FiWroFJTnR4/fD9CsusnAn4qRUcbb5jFnZSd6uw=="], + "@playwright/test": ["@playwright/test@1.52.0", "", { "dependencies": { "playwright": "1.52.0" }, "bin": { "playwright": "cli.js" } }, "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="], @@ -292,7 +296,7 @@ "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], - "@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], + "@types/node": ["@types/node@22.15.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA=="], "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], @@ -592,7 +596,7 @@ "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -990,6 +994,10 @@ "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + "playwright": ["playwright@1.52.0", "", { "dependencies": { "playwright-core": "1.52.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw=="], + + "playwright-core": ["playwright-core@1.52.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], @@ -1350,11 +1358,15 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "sitemap/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], + "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], - "vite/@types/node": ["@types/node@22.15.29", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ=="], + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], diff --git a/package.json b/package.json index 7f06a06..fe36892 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint .", + "test": "playwright test", + "test:ui": "playwright test --ui", "prepare": "husky || true" }, "dependencies": { @@ -22,6 +24,8 @@ "tailwindcss": "^4.1.8" }, "devDependencies": { + "@playwright/test": "^1.52.0", + "@types/node": "^22.15.30", "@typescript-eslint/parser": "8.33.1", "eslint": "9.28.0", "eslint-plugin-astro": "1.3.1", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..b9d5922 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,79 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./tests", + /* 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:4321", + + /* 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: "bun run build && bun run preview", + url: "http://localhost:4321", + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/src/components/server/Uptime.astro b/src/components/server/Uptime.astro index ff65f3e..c034ec3 100644 --- a/src/components/server/Uptime.astro +++ b/src/components/server/Uptime.astro @@ -10,11 +10,14 @@ interface Props { } const { name } = Astro.props; +let show = true; + // Ensure ENV variables are set if (!KUMA_URL || !KUMA_API_KEY) { - Astro.response.status = 500; - Astro.response.statusText = "Internal Server Error"; - return; + console.warn( + "Uptime Kuma URL or API Key is not set. Skipping Uptime component.", + ); + show = false; } // Fetch the metrics from Uptime Kuma @@ -25,9 +28,10 @@ const response = await fetch(`${KUMA_URL}/metrics`, { }); if (!response.ok) { - Astro.response.status = response.status; - Astro.response.statusText = response.statusText; - return; + console.warn( + `Failed to fetch metrics from Uptime Kuma: ${response.status} ${response.statusText}`, + ); + show = false; } // Parse the metrics to find the status of the monitor with the given name @@ -50,9 +54,8 @@ const status = metrics .map((m) => m.status)[0]; if (status == null) { - Astro.response.status = 404; - Astro.response.statusText = "Not Found"; - return; + console.warn(`Monitor with name "${name}" not found in Uptime Kuma metrics.`); + show = false; } // Map the status to a color @@ -72,11 +75,17 @@ Astro.response.headers.set( ); --- -<span class="absolute -top-1.5 -right-1 w-2.5 h-2.5"> - <span - class="absolute inset-0 rounded-full bg-gray-500 opacity-75 motion-safe:animate-ping" - style={style}></span> - <span - class="relative block w-2.5 h-2.5 rounded-full bg-gray-500 shadow shadow-green-400/50" - style={style}></span> -</span> +{ + show && ( + <span class="absolute -top-1.5 -right-1 w-2.5 h-2.5"> + <span + class="absolute inset-0 rounded-full bg-gray-500 opacity-75 motion-safe:animate-ping" + style={style} + /> + <span + class="relative block w-2.5 h-2.5 rounded-full bg-gray-500 shadow shadow-green-400/50" + style={style} + /> + </span> + ) +} diff --git a/tests/index.spec.ts b/tests/index.spec.ts new file mode 100644 index 0000000..b804a2a --- /dev/null +++ b/tests/index.spec.ts @@ -0,0 +1,48 @@ +import { + test, + expect, + type Locator, + type BrowserContext, +} from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.goto("/"); +}); + +test("has title", async ({ page }) => { + await expect(page).toHaveTitle("Anshul Gupta"); +}); + +test("has header", async ({ page }) => { + let header = page.locator("header"); + await expect(header).toBeVisible(); + await expect(header.locator("div").first()).toHaveText("ANSHUL GUPTA (7)"); + await expect(header.locator("div").last()).toHaveText("ANSHUL GUPTA (7)"); + await expect(header.locator("h1")).toHaveText( + "Miscellaneous Information Manual", + ); +}); + +test("has Email link", async ({ page }) => { + let emailLink = page.getByRole("link", { name: "ansg191@anshulg.com" }); + await expect(emailLink).toBeVisible(); + await expect(emailLink).toHaveText("ansg191@anshulg.com"); + await expect(emailLink).toHaveAttribute("href", "mailto:ansg191@anshulg.com"); +}); + +test("has GitHub link", async ({ page, context }) => { + let ghLink = page.getByRole("link", { name: "ansg191" }).nth(1); + await checkLinkNewTab(context, ghLink, "https://github.com/ansg191"); +}); + +async function checkLinkNewTab( + ctx: BrowserContext, + link: Locator, + expUrl: string, +) { + const pagePromise = ctx.waitForEvent("page"); + await expect(link).toBeVisible(); + await link.click(); + const newPage = await pagePromise; + await expect(newPage).toHaveURL(expUrl); +} |