diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 13755f4..57de3bc 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,29 +1,32 @@ -name: Build pull request -on: [pull_request] +name: Test pull request +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review] jobs: - build: + test-node: + name: Node runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x, 20.x] # Run only on non-merged non-draft mergeable pull requests if: | - !( - (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize') - && !github.event.pull_request.draft + ( + !github.event.pull_request.draft && !github.event.pull_request.merged && github.event.pull_request.merge_commit_sha != null ) steps: - name: Check out merge commit of pull request - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.merge_commit_sha }} - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + submodules: true + - name: Use Node.js 20 + uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} - - name: npm install and test - run: | - npm install - npm test + node-version: 20 + cache: npm + - run: npm ci + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + - run: npm run test:types + - run: npm run test:unit + - run: npm run test:wpt:node diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 44bee52..5179fcf 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,22 +1,54 @@ -name: Build on push +name: Test on push on: [push] jobs: - build: + test-node: + name: Node ${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: - node-version: [18.x, 20.x] + node-version: [18, 20] # Skip job if commit message contains "[skip ci]" if: | !contains(github.event.head_commit.message, '[skip ci]') steps: - name: Check out commit - uses: actions/checkout@v2 + uses: actions/checkout@v3 + with: + submodules: true - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - name: npm install and test - run: | - npm install - npm test + cache: npm + - run: npm ci + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + - run: npm run test:types + - run: npm run test:unit + - run: npm run test:wpt:node + + test-browser: + name: ${{ matrix.browser }} + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/playwright:v1.40.0-jammy + strategy: + fail-fast: false + matrix: + browser: [chromium, firefox] + if: | + !contains(github.event.head_commit.message, '[skip ci]') + steps: + - name: Check out commit + uses: actions/checkout@v3 + with: + submodules: true + - name: Use Node.js 20 + uses: actions/setup-node@v3 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run test:wpt:${{ matrix.browser }} + env: + HOME: /root diff --git a/package-lock.json b/package-lock.json index 4174878..fcb442a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,9 @@ "eslint": "^8.56.0", "jasmine": "^5.1.0", "micromatch": "^4.0.5", + "minimist": "^1.2.5", + "playwright": "^1.14.1", + "recursive-readdir": "^2.2.2", "rollup": "^4.9.2", "tslib": "^2.6.2", "typescript": "^5.3.3", @@ -87,6 +90,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", @@ -110,6 +135,28 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -428,15 +475,6 @@ } } }, - "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/@rollup/plugin-typescript": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz", @@ -889,30 +927,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { "version": "6.17.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", @@ -1104,12 +1118,6 @@ "yallist": "^2.1.2" } }, - "node_modules/async-cache/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1154,13 +1162,12 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -1596,6 +1603,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -1846,9 +1875,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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, "optional": true, @@ -1911,30 +1940,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -2400,15 +2405,12 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/magic-string": { @@ -2479,15 +2481,27 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { @@ -2655,15 +2669,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2685,6 +2690,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", + "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", + "dev": true, + "dependencies": { + "playwright-core": "1.40.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", + "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2776,6 +2811,28 @@ "node": ">=6.0.0" } }, + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2842,6 +2899,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2862,6 +2929,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/rollup": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.2.tgz", @@ -2973,6 +3052,33 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3635,9 +3741,9 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, "node_modules/yargs": { diff --git a/package.json b/package.json index 1556a07..aa66d14 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,10 @@ "types": "types/polyfill.d.ts", "scripts": { "test": "npm run test:types && npm run test:unit && npm run test:wpt", - "test:wpt": "node --expose_gc ./test/run-web-platform-tests.js", - "pretest:wpt": "git submodule update --init --recursive", + "test:wpt": "npm run test:wpt:node && npm run test:wpt:chromium && npm run test:wpt:firefox", + "test:wpt:node": "node --expose_gc ./test/wpt/node/run.js", + "test:wpt:chromium": "node ./test/wpt/browser/run.js --browser chromium", + "test:wpt:firefox": "node ./test/wpt/browser/run.js --browser firefox", "test:types": "tsc -p ./test/types/tsconfig.json", "test:unit": "jasmine --config=test/unit/jasmine.json", "lint": "eslint \"src/**/*.ts\"", @@ -61,6 +63,9 @@ "eslint": "^8.56.0", "jasmine": "^5.1.0", "micromatch": "^4.0.5", + "minimist": "^1.2.5", + "playwright": "^1.14.1", + "recursive-readdir": "^2.2.2", "rollup": "^4.9.2", "tslib": "^2.6.2", "typescript": "^5.3.3", diff --git a/test/wpt/browser/run.js b/test/wpt/browser/run.js new file mode 100644 index 0000000..a189387 --- /dev/null +++ b/test/wpt/browser/run.js @@ -0,0 +1,219 @@ +/* eslint-disable no-console */ + +const path = require('path'); +const http = require('http'); +const { promisify } = require('util'); +const micromatch = require('micromatch'); +const { chromium, firefox } = require('playwright'); +const minimist = require('minimist'); +const recursiveReadDir = require('recursive-readdir'); +const { setupServer } = require('./server.js'); +const consoleReporter = require('wpt-runner/lib/console-reporter.js'); +const { SourceFile } = require('wpt-runner/lib/internal/sourcefile.js'); +const { FilteringReporter } = require('../shared/filtering-reporter.js'); +const { + excludedTestsBase, + mergeIgnoredFailures, + ignoredFailuresBase, + ignoredFailuresMinified, + ignoredFailuresES5, + ignoredFailuresES6 +} = require('../shared/exclusions'); + +const serverCloseAsync = promisify(http.Server.prototype.close); +const recursiveReadDirAsync = promisify(recursiveReadDir); + +const argv = minimist(process.argv.slice(2), { + string: ['browser'] +}); + +main().catch(e => { + console.error(e.stack); + process.exitCode = 1; +}); + +async function main() { + const browserType = argv.browser || 'chromium'; + const includedTests = argv._.length > 0 ? argv._ : ['**/*.html']; + const excludedTests = [...excludedTestsBase]; + + const wptPath = path.resolve(__dirname, '../../web-platform-tests'); + const results = []; + let server; + let browser; + try { + server = setupServer(wptPath, { rootURL: '/' }); + const urlPrefix = `http://127.0.0.1:${server.address().port}`; + console.log(`Server running at ${urlPrefix}`); + + browser = await browserTypeByName(browserType).launch(); + const testOptions = { includedTests, excludedTests, browser, wptPath, urlPrefix }; + results.push(await runTests({ + ...testOptions, + entryFile: 'polyfill.es2018.min.js', + ignoredFailures: mergeIgnoredFailures(ignoredFailuresBase, ignoredFailuresMinified) + })); + results.push(await runTests({ + ...testOptions, + entryFile: 'polyfill.es6.min.js', + ignoredFailures: mergeIgnoredFailures(ignoredFailuresES6, ignoredFailuresMinified) + })); + results.push(await runTests({ + ...testOptions, + entryFile: 'polyfill.min.js', + ignoredFailures: mergeIgnoredFailures(ignoredFailuresES5, ignoredFailuresMinified) + })); + } finally { + if (browser) { + await browser.close(); + } + if (server) { + await serverCloseAsync.call(server); + } + } + + const failures = results.reduce((sum, result) => sum + result.failures, 0); + for (const { entryFile, testResults } of results) { + console.log(`> ${entryFile}`); + console.log(` * ${testResults.passed} passed`); + console.log(` * ${testResults.failed} failed`); + console.log(` * ${testResults.ignored} ignored`); + } + + process.exitCode = failures; +} + +async function runTests({ entryFile, includedTests, excludedTests, ignoredFailures, browser, wptPath, urlPrefix }) { + const entryPath = path.resolve(__dirname, `../../../dist/${entryFile}`); + const testsBase = '/streams/'; + const testsPath = path.resolve(wptPath, 'streams'); + + const includeMatcher = micromatch.matcher(includedTests); + const excludeMatcher = micromatch.matcher(excludedTests); + const workerTestPattern = /\.(?:dedicated|shared|service)worker(?:\.https)?\.html$/; + const testPaths = (await readTestPaths(testsPath)).filter(testPath => { + // Ignore the worker versions + if (workerTestPattern.test(testPath)) { + return false; + } + return includeMatcher(testPath) && !excludeMatcher(testPath); + }); + + const reporter = new FilteringReporter(consoleReporter, ignoredFailures); + + console.log(`>>> ${entryFile}`); + + let context; + try { + context = await browser.newContext(); + await context.addInitScript({ path: entryPath }); + await context.route(`${urlPrefix}/resources/testharnessreport.js`, route => { + route.fulfill({ + body: ` + window.fetch_tests_from_worker = () => undefined; + window.add_result_callback(({ name, status, message, stack }) => { + window.__wptResultCallback({ name, status, message, stack }); + }); + window.add_completion_callback((tests, { status, message, stack }) => { + window.__wptCompletionCallback({ status, message, stack }); + }); + ` + }); + }); + for (const testPath of testPaths) { + reporter.startSuite(testPath); + const page = await context.newPage(); + const testUrl = `${urlPrefix}${testsBase}${testPath}`; + await runTest(page, testUrl, reporter); + await page.close(); + } + } finally { + if (context) { + await context.close(); + } + } + + const wptFailures = 0; + const testResults = reporter.getResults(); + const failures = Math.max(testResults.failed, wptFailures - testResults.ignored); + + console.log(); + + return { entryFile, failures, testResults }; +} + +async function runTest(page, testUrl, reporter) { + let hasFailed = false; + let resolveDone; + const donePromise = new Promise(resolve => { + resolveDone = resolve; + }); + + await page.exposeFunction('__wptResultCallback', test => { + if (test.status === 0) { + reporter.pass(test.name); + } else if (test.status === 1) { + reporter.fail(`${test.name}\n`); + reporter.reportStack(`${test.message}\n${test.stack}`); + hasFailed = true; + } else if (test.status === 2) { + reporter.fail(`${test.name} (timeout)\n`); + reporter.reportStack(`${test.message}\n${test.stack}`); + hasFailed = true; + } else if (test.status === 3) { + reporter.fail(`${test.name} (incomplete)\n`); + reporter.reportStack(`${test.message}\n${test.stack}`); + hasFailed = true; + } else if (test.status === 4) { + reporter.fail(`${test.name} (precondition failed)\n`); + reporter.reportStack(`${test.message}\n${test.stack}`); + hasFailed = true; + } else { + reporter.fail(`unknown test status: ${test.status}`); + hasFailed = true; + } + }); + + await page.exposeFunction('__wptCompletionCallback', harnessStatus => { + if (harnessStatus.status === 0) { + resolveDone(!hasFailed); + } else if (harnessStatus.status === 1) { + reporter.fail('test harness threw unexpected error'); + reporter.reportStack(`${harnessStatus.message}\n${harnessStatus.stack}`); + resolveDone(false); + } else if (harnessStatus.status === 2) { + reporter.fail('test harness should not timeout'); + resolveDone(false); + } else if (harnessStatus.status === 4) { + reporter.fail('test harness precondition failed'); + reporter.reportStack(`${harnessStatus.message}\n${harnessStatus.stack}`); + resolveDone(false); + } else { + reporter.fail(`unknown test harness status: ${harnessStatus.status}`); + resolveDone(false); + } + }); + + await page.goto(testUrl); + return await donePromise; +} + +function browserTypeByName(name) { + switch (name) { + case 'firefox': + return firefox; + case 'chromium': + default: + return chromium; + } +} + +async function readTestPaths(testsPath) { + const fileNames = await recursiveReadDirAsync(testsPath); + const testFilePaths = []; + for (const fileName of fileNames) { + const sourceFile = new SourceFile(testsPath, path.relative(testsPath, fileName)); + testFilePaths.push(...sourceFile.testPaths()); + } + return testFilePaths.sort(); +} diff --git a/test/wpt/browser/server.js b/test/wpt/browser/server.js new file mode 100644 index 0000000..cbf216c --- /dev/null +++ b/test/wpt/browser/server.js @@ -0,0 +1,85 @@ +const http = require('http'); +const path = require('path'); +const fs = require('fs'); +const { URL } = require('url'); +const st = require('st'); +const { AnyHtmlHandler, WindowHandler } = require('wpt-runner/lib/internal/serve.js'); + +const testharnessPath = require.resolve('wpt-runner/testharness/testharness.js'); +const idlharnessPath = require.resolve('wpt-runner/testharness/idlharness.js'); +const webidl2jsPath = require.resolve('wpt-runner/testharness/webidl2/lib/webidl2.js'); +const testdriverDummyPath = require.resolve('wpt-runner/lib/testdriver-dummy.js'); + +function setupServer(testsPath, { + rootURL = '/' +}) { + if (!rootURL.startsWith('/')) { + rootURL = '/' + rootURL; + } + if (!rootURL.endsWith('/')) { + rootURL += '/'; + } + + const staticFileServer = st({ path: testsPath, url: rootURL, passthrough: true }); + + const routes = [ + ['.window.html', new WindowHandler(testsPath, rootURL)], + ['.any.html', new AnyHtmlHandler(testsPath, rootURL)] + ]; + + return http.createServer((req, res) => { + const { pathname } = new URL(req.url, `http://${req.headers.host}`); + + for (const [pathNameSuffix, handler] of routes) { + if (pathname.endsWith(pathNameSuffix)) { + handler.handleRequest(req, res); + return; + } + } + + switch (pathname) { + case '/resources/testharness.js': { + fs.createReadStream(testharnessPath).pipe(res); + break; + } + + case '/resources/idlharness.js': { + fs.createReadStream(idlharnessPath).pipe(res); + break; + } + + case '/resources/WebIDLParser.js': { + fs.createReadStream(webidl2jsPath).pipe(res); + break; + } + + case '/resources/testharnessreport.js': { + res.end(''); + break; + } + + case '/resources/testdriver.js': { + fs.createReadStream(testdriverDummyPath).pipe(res); + break; + } + + case '/resources/testdriver-vendor.js': { + res.end(''); + break; + } + + case '/favicon.ico': { + res.end(''); + break; + } + + default: { + staticFileServer(req, res, () => { + throw new Error(`Unexpected URL: ${req.url}`); + }); + } + } + }).listen(); +} + +exports.setupServer = setupServer; diff --git a/test/run-web-platform-tests.js b/test/wpt/node/run.js similarity index 50% rename from test/run-web-platform-tests.js rename to test/wpt/node/run.js index 7ca9b5f..1d29209 100644 --- a/test/run-web-platform-tests.js +++ b/test/wpt/node/run.js @@ -8,8 +8,17 @@ const { promisify } = require('util'); const micromatch = require('micromatch'); const wptRunner = require('wpt-runner'); const consoleReporter = require('wpt-runner/lib/console-reporter.js'); -const { FilteringReporter } = require('./wpt-util/filtering-reporter.js'); +const { FilteringReporter } = require('../shared/filtering-reporter.js'); const allSettled = require('@ungap/promise-all-settled'); +const { + excludedTestsNonES2018, + excludedTestsBase, + ignoredFailuresBase, + ignoredFailuresMinified, + ignoredFailuresES5, + ignoredFailuresES6, + mergeIgnoredFailures +} = require('../shared/exclusions'); const readFileAsync = promisify(fs.readFile); const queueMicrotask = global.queueMicrotask || (fn => Promise.resolve().then(fn)); @@ -34,103 +43,46 @@ main().catch(e => { async function main() { const supportsES2018 = runtimeSupportsAsyncGenerators(); + const includedTests = process.argv.length >= 3 ? process.argv.slice(2) : ['**/*.html']; const excludedTests = [ - // We cannot polyfill TransferArrayBuffer yet, so disable tests for detached array buffers - // See https://github.com/MattiasBuelens/web-streams-polyfill/issues/3 - 'readable-byte-streams/bad-buffers-and-views.any.html', - 'readable-byte-streams/enqueue-with-detached-buffer.window.html', - 'readable-byte-streams/non-transferable-buffers.any.html', - // Disable tests for different size functions per realm, since they need a working