diff --git a/.github/workflows/js-lint.yml b/.github/workflows/js-lint.yml index 94d50a4..dc2f271 100644 --- a/.github/workflows/js-lint.yml +++ b/.github/workflows/js-lint.yml @@ -11,7 +11,8 @@ on: - 'package.json' - 'bun.lock' - 'biome.json' - - 'tsconfig.json' + - 'tsconfig*.json' + - '.github/workflows/js-lint.yml' pull_request: paths: - '**/*.js' @@ -20,23 +21,24 @@ on: - 'package.json' - 'bun.lock' - 'biome.json' - - 'tsconfig.json' + - 'tsconfig*.json' + - '.github/workflows/js-lint.yml' workflow_dispatch: permissions: contents: read jobs: - lint: - name: Biome + TypeScript + frontend: + name: Biome + TypeScript + Bun tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json - run: bun install --frozen-lockfile - - run: bun run lint - - - run: bunx tsc --noEmit + - run: bun run check:frontend diff --git a/README.md b/README.md index 2618e4c..36c2eb7 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,11 @@ From [`package.json`](package.json): | `bun run build` | Minify JS/CSS to `assets/`, copy TinyMCE and tom-select vendor files | | `bun run build:package` | Build assets and create the validated `release/teksttv/` directory plus versioned ZIP | | `bun run dev` | Watch JS and CSS | -| `bun run lint` | PHPCS + Biome on `resources/` | +| `bun run check` | PHPCS plus all frontend checks | +| `bun run check:frontend` | Biome, TypeScript and Bun unit tests | +| `bun run lint:js` | Biome on maintained JS, TS and CSS sources/tests | | `bun run lint:fix` | PHPCBF + Biome `--write` | +| `bun run typecheck`| TypeScript type checking without emitting files | | `bun run analyse` | PHPStan | | `bun run test` | PHPUnit (unit) | | `bun run env:start`| Build + package the artifact and boot WordPress via [`wp-env`](https://www.npmjs.com/package/@wordpress/env) (needs Docker) | diff --git a/biome.json b/biome.json index 75f2df2..153a771 100644 --- a/biome.json +++ b/biome.json @@ -1,24 +1,31 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.9/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.5/schema.json", "files": { - "includes": ["**/resources/ts/**/*.ts"] + "includes": ["resources/**", "tests/**", "playwright.config.ts"] }, "linter": { "enabled": true, "rules": { - "recommended": true, "complexity": { - "noForEach": "off", - "useOptionalChain": "off" + "noImportantStyles": "off" }, "correctness": { "noUnusedVariables": "warn" - }, - "style": { - "useTemplate": "off" } } }, + "overrides": [ + { + "includes": ["resources/css/partials/blocks.css"], + "linter": { + "rules": { + "style": { + "noDescendingSpecificity": "off" + } + } + } + } + ], "formatter": { "indentStyle": "space", "indentWidth": 4, diff --git a/bun.lock b/bun.lock index 1f5865e..614599b 100644 --- a/bun.lock +++ b/bun.lock @@ -3,17 +3,19 @@ "configVersion": 1, "workspaces": { "": { - "name": "tmp-teksttv-wp-plugin", + "name": "teksttv-wp-plugin", "dependencies": { "alpinejs": "^3.15.12", "sortablejs": "^1.15.7", "tom-select": "^2.6.2", }, "devDependencies": { - "@biomejs/biome": "^2.5.5", + "@biomejs/biome": "2.5.5", "@playwright/test": "^1.62.0", + "@types/bun": "1.3.14", "@types/sortablejs": "^1.15.9", "@wordpress/env": "^11.11.0", + "typescript": "7.0.2", }, }, }, @@ -182,6 +184,8 @@ "@types/btoa-lite": ["@types/btoa-lite@1.0.2", "", {}, "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], "@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="], @@ -192,12 +196,52 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="], + "@types/node": ["@types/node@26.1.2", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg=="], "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], "@types/sortablejs": ["@types/sortablejs@1.15.9", "", {}, "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ=="], + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + "@vue/reactivity": ["@vue/reactivity@3.1.5", "", { "dependencies": { "@vue/shared": "3.1.5" } }, "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg=="], "@vue/shared": ["@vue/shared@3.1.5", "", {}, "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="], @@ -262,6 +306,8 @@ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], @@ -668,6 +714,8 @@ "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "universal-github-app-jwt": ["universal-github-app-jwt@1.2.0", "", { "dependencies": { "@types/jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.2" } }, "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g=="], diff --git a/package.json b/package.json index a7a2eb5..87c2fe9 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,19 @@ { "name": "teksttv-wp-plugin", "private": true, + "packageManager": "bun@1.3.14", "scripts": { "build": "bun run build:js && bun run build:css && bun run build:static", "build:js": "bun build resources/ts/admin.ts --outdir assets --minify-whitespace --minify-syntax", "build:css": "bun build resources/css/admin.css --outdir assets --minify", "build:static": "cp resources/ts/tinymce-separator.js assets/ && cp resources/css/tinymce-content.css assets/ && cp node_modules/tom-select/dist/css/tom-select.default.min.css assets/ && cp node_modules/tom-select/dist/js/tom-select.complete.min.js assets/", "dev": "bun run build:js -- --watch & bun run build:css -- --watch", - "check": "bun run lint:php && bun run lint:js", - "lint": "biome check resources/", + "check": "bun run lint:php && bun run check:frontend", + "check:frontend": "bun run lint:js && bun run typecheck && bun run test:js", "lint:php": "vendor/bin/phpcs", - "lint:js": "biome check resources/", - "lint:fix": "vendor/bin/phpcbf; bunx biome check --write resources/", + "lint:js": "biome check .", + "lint:fix": "vendor/bin/phpcbf; p=$?; biome check --write . && [ $p -le 1 ]", + "typecheck": "tsc && tsc -p tsconfig.tests.json", "analyse": "vendor/bin/phpstan analyse --memory-limit=1G", "test": "vendor/bin/phpunit", "test:js": "bun test tests/js", @@ -27,9 +29,11 @@ "tom-select": "^2.6.2" }, "devDependencies": { - "@biomejs/biome": "^2.5.5", + "@biomejs/biome": "2.5.5", "@playwright/test": "^1.62.0", + "@types/bun": "1.3.14", "@types/sortablejs": "^1.15.9", - "@wordpress/env": "^11.11.0" + "@wordpress/env": "^11.11.0", + "typescript": "7.0.2" } } diff --git a/resources/css/admin.css b/resources/css/admin.css index 08ad72e..1cfdcfa 100644 --- a/resources/css/admin.css +++ b/resources/css/admin.css @@ -1,7 +1,7 @@ -@import './partials/base.css'; -@import './partials/settings.css'; -@import './partials/blocks.css'; -@import './partials/post-meta.css'; -@import './partials/preview.css'; -@import './partials/ticker.css'; -@import './partials/audit.css'; +@import "./partials/base.css"; +@import "./partials/settings.css"; +@import "./partials/blocks.css"; +@import "./partials/post-meta.css"; +@import "./partials/preview.css"; +@import "./partials/ticker.css"; +@import "./partials/audit.css"; diff --git a/resources/css/tinymce-content.css b/resources/css/tinymce-content.css index c698d05..4559a8c 100644 --- a/resources/css/tinymce-content.css +++ b/resources/css/tinymce-content.css @@ -1,7 +1,9 @@ body { margin: 0 !important; padding: 6px 8px !important; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", + sans-serif; font-size: 14px; line-height: 1.6; } diff --git a/resources/ts/modules/utils.ts b/resources/ts/modules/utils.ts index 8717306..f059c90 100644 --- a/resources/ts/modules/utils.ts +++ b/resources/ts/modules/utils.ts @@ -37,7 +37,7 @@ export function splitPages(html: string, enabled = true): string[] { /** Debounce `fn`: each call restarts the timer; only the last call within `ms` runs. */ export function debounce(fn: () => void, ms: number): () => void { - let timer: ReturnType | undefined; + let timer: number | undefined; return () => { clearTimeout(timer); timer = window.setTimeout(fn, ms); diff --git a/resources/ts/tinymce-separator.js b/resources/ts/tinymce-separator.js index e575a36..e55864a 100644 --- a/resources/ts/tinymce-separator.js +++ b/resources/ts/tinymce-separator.js @@ -1,4 +1,4 @@ -// biome-ignore: TinyMCE 4 calls plugin callbacks with `new`, so these MUST be regular functions (not arrow functions). +// biome-ignore-all lint/complexity/useArrowFunction: Preserve TinyMCE 4-compatible legacy function syntax. (function () { if (typeof tinymce === 'undefined') return; diff --git a/tests/e2e/global-setup.ts b/tests/e2e/global-setup.ts index 09ec78f..559c1da 100644 --- a/tests/e2e/global-setup.ts +++ b/tests/e2e/global-setup.ts @@ -1,6 +1,6 @@ import { mkdirSync } from 'node:fs'; import { dirname } from 'node:path'; -import { type FullConfig, chromium } from '@playwright/test'; +import { chromium, type FullConfig } from '@playwright/test'; import { ADMIN_STORAGE_STATE, login } from './helpers'; import { reseedFixtures } from './reseed-fixtures'; diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts index 7200760..592ef5a 100644 --- a/tests/e2e/helpers.ts +++ b/tests/e2e/helpers.ts @@ -1,4 +1,4 @@ -import { type Locator, type Page, expect } from '@playwright/test'; +import { expect, type Locator, type Page } from '@playwright/test'; /** Admin session saved by global-setup and loaded by every test (see playwright.config.ts). */ export const ADMIN_STORAGE_STATE = '.playwright/auth/admin.json'; @@ -9,9 +9,7 @@ export async function login(page: Page, username: string, password: string): Pro await page.fill('#user_login', username); await page.fill('#user_pass', password); await page.locator('#wp-submit').click(); - await expect(page).toHaveURL( - (url) => url.pathname === '/wp-admin' || url.pathname.startsWith('/wp-admin/'), - ); + await expect(page).toHaveURL((url) => url.pathname === '/wp-admin' || url.pathname.startsWith('/wp-admin/')); await expect(page.locator('#wpadminbar')).toBeVisible(); } diff --git a/tests/e2e/interactions.spec.ts b/tests/e2e/interactions.spec.ts index f4b610c..4c4d0ff 100644 --- a/tests/e2e/interactions.spec.ts +++ b/tests/e2e/interactions.spec.ts @@ -1,4 +1,4 @@ -import { type Locator, type Page, expect, test } from '@playwright/test'; +import { expect, type Locator, type Page, test } from '@playwright/test'; import { addLoopBlock, addTickerBlock, submitAndReload } from './helpers'; import { reseedFixtures } from './reseed-fixtures'; @@ -6,13 +6,15 @@ const LOOP_URL = '/wp-admin/admin.php?page=teksttv-loop-tv1'; async function expectSequentialNames(root: Locator, itemSelector: string, prefix: string): Promise { // One evaluate round-trip: every item's field names, in DOM order. - const itemNames = await root.locator(itemSelector).evaluateAll((items) => - items.map((item) => - Array.from(item.querySelectorAll('input[name], select[name], textarea[name], [data-name]')).map( - (field) => field.getAttribute('name') ?? field.getAttribute('data-name'), + const itemNames = await root + .locator(itemSelector) + .evaluateAll((items) => + items.map((item) => + Array.from(item.querySelectorAll('input[name], select[name], textarea[name], [data-name]')).map( + (field) => field.getAttribute('name') ?? field.getAttribute('data-name'), + ), ), - ), - ); + ); expect(itemNames.length, `${prefix} should contain at least one item`).toBeGreaterThan(0); itemNames.forEach((names, index) => { @@ -80,9 +82,9 @@ test.describe('admin interaction contracts', () => { .evaluate((button) => { button.dispatchEvent(new MouseEvent('click', { bubbles: true })); const block = button.closest('.teksttv-block'); - const controls = block?.querySelectorAll< - HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement - >('input, select, textarea'); + const controls = block?.querySelectorAll( + 'input, select, textarea', + ); return Array.from(controls ?? [], (control) => control.disabled); }); expect(disabledStates.length).toBeGreaterThan(0); diff --git a/tests/e2e/media.spec.ts b/tests/e2e/media.spec.ts index aedaeed..eb1ad06 100644 --- a/tests/e2e/media.spec.ts +++ b/tests/e2e/media.spec.ts @@ -1,4 +1,4 @@ -import { type Page, expect, test } from '@playwright/test'; +import { expect, type Page, test } from '@playwright/test'; import { addLoopBlock } from './helpers'; type EditorWindow = Window & { @@ -56,9 +56,7 @@ async function openFixturePostEditor(page: Page): Promise { await metaBoxesButton.focus(); await page.keyboard.press('Enter'); } - await expect - .poll(() => metaBoxesButton.getAttribute('aria-expanded')) - .toBe('true'); + await expect.poll(() => metaBoxesButton.getAttribute('aria-expanded')).toBe('true'); } // No reseed hooks here: none of these tests submit a form or save the post, diff --git a/tsconfig.json b/tsconfig.json index 4e1e87c..6739469 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleResolution": "bundler", + "types": [], "strict": true, "noEmit": true, "skipLibCheck": true diff --git a/tsconfig.tests.json b/tsconfig.tests.json new file mode 100644 index 0000000..e4514a6 --- /dev/null +++ b/tsconfig.tests.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["bun"] + }, + "include": ["tests", "playwright.config.ts"] +}