From bc52139b657d9ffddc3a132ab3a3ed965c33e2e1 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Wed, 21 Jun 2023 16:07:17 -0700 Subject: [PATCH 01/12] [cypress] Combine rollup config Rollup supports a config array, which allows us to invoke it just once. This also moves config-wrapper-sync.ts into the main build step, which allows it to share code with the rest of the CommonJS build, yielding a smaller bundle size. --- packages/cypress-plugin/package.json | 4 +- packages/cypress-plugin/rollup.config.mjs | 129 +++++++++++++--------- 2 files changed, 78 insertions(+), 55 deletions(-) diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index 7f4d6f7..f1aa35c 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -90,9 +90,7 @@ "cypress": "11.2 - 12" }, "scripts": { - "build": "yarn clean && tsc --noEmit && tsc --noEmit -p src && yarn build:cjs && yarn build:esm", - "build:cjs": "rollup --config --dir dist && rollup --config --input src/config-wrapper-sync.ts --file dist/config-wrapper-sync.js --chunkFileNames \"[name]-[hash]-sync.js\"", - "build:esm": "rollup --config --input src/config-wrapper.ts --file dist/config-wrapper.mjs --format es --chunkFileNames \"[name]-[hash].mjs\"", + "build": "yarn clean && tsc --noEmit && tsc --noEmit -p src && rollup --config", "clean": "rm -rf dist/", "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --useStderr --verbose" } diff --git a/packages/cypress-plugin/rollup.config.mjs b/packages/cypress-plugin/rollup.config.mjs index 32bb23a..95dcabd 100644 --- a/packages/cypress-plugin/rollup.config.mjs +++ b/packages/cypress-plugin/rollup.config.mjs @@ -5,60 +5,85 @@ import pluginJson from "@rollup/plugin-json"; import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginTypescript from "@rollup/plugin-typescript"; +/** + * Bundle the internal @unflakable/plugins-common package, along with dependencies used by + * vendored Cypress code that Cypress itself also bundles (i.e., doesn't list in its public + * package.json as deps) in dist/, but leave most other imported packages as an external. Internal + * modules begin with `.` or `/`. We don't include `term-size` here because it depends on a + * bundled vendor/ directory that rollup doesn't include. + * + * @type {import("rollup").IsExternal} + */ +const isExternal = (id) => + !id.startsWith(".") && + !id.startsWith("/") && + !id.startsWith("src/") && + !id.startsWith("@unflakable/plugins-common/") && + ![ + // Avoid having skip-tests depend on @unflakable/js-api, which could pull in Node dependencies + // that Webpack v5 doesn't bundle by default. It's also unclear which versions of Webpack + // support sub-path exports from package.json like this. To avoid requiring any changes to + // the user's Webpack config, we try to make skip-tests self-contained and easy to import. + "@unflakable/js-api/consts", + "@unflakable/plugins-common", + "widest-line", + ].includes(id); + +const plugins = [ + pluginCommonJs(), + pluginJson(), + pluginNodeResolve({ preferBuiltins: true }), + pluginTypescript({ tsconfig: "src/tsconfig.json" }), +]; + +const treeshake = { + // Assume internal modules do not have side effects when they're imported. This helps remove + // unnecessary require()'s from the transpiled code. + moduleSideEffects: (id, external) => external, +}; + /** * @type {import("rollup").NormalizedInputOptions} */ -export default { - // NB: We exclude src/config-wrapper.ts since that needs to be compiled as an ESM target in - // order to be able to import user cypress.config.js files for projects that use ESM. The reason - // is that Cypress loads our config file and expects the default export to be the config - // object, which requires us to load the user config file when our script loads. Dynamic - // import() of ESM (require() can't import ESM modules) is async, but CommonJS doesn't support - // await at the top level of a file (ESM does). To summarize: (1) code that's imported by Cypress - // (with the exception of the config file, due to (2)) should be CommonJS, while (2) code that - // imports user code should be ESM so that it supports both CommonJS and ESM user code. - input: [ - "src/index.ts", - "src/main.ts", - "src/reporter.ts", - "src/skip-tests.ts", - ], - output: { - format: "cjs", - /** - * @param {import("rollup").PreRenderedChunk} chunk - * @returns {string} - */ - banner: (chunk) => (chunk.name === "main" ? "#!/usr/bin/env node" : ""), +export default [ + { + // NB: We exclude src/config-wrapper.ts since that needs to be compiled as an ESM target in + // order to be able to import user cypress.config.js files for projects that use ESM. The reason + // is that Cypress loads our config file and expects the default export to be the config + // object, which requires us to load the user config file when our script loads. Dynamic + // import() of ESM (require() can't import ESM modules) is async, but CommonJS doesn't support + // await at the top level of a file (ESM does). To summarize: (1) code that's imported by Cypress + // (with the exception of the config file, due to (2)) should be CommonJS, while (2) code that + // imports user code should be ESM so that it supports both CommonJS and ESM user code. + input: [ + "src/config-wrapper-sync.ts", + "src/index.ts", + "src/main.ts", + "src/reporter.ts", + "src/skip-tests.ts", + ], + output: { + format: "cjs", + dir: "dist", + /** + * @param {import("rollup").PreRenderedChunk} chunk + * @returns {string} + */ + banner: (chunk) => (chunk.name === "main" ? "#!/usr/bin/env node" : ""), + }, + external: isExternal, + plugins, + treeshake, }, - // Bundle the internal @unflakable/plugins-common package, along with dependencies used by - // vendored Cypress code that Cypress itself also bundles (i.e., doesn't list in its public - // package.json as deps) in dist/, but leave most other imported packages as an external. Internal - // modules begin with `.` or `/`. We don't include `term-size` here because it depends on a - // bundled vendor/ directory that rollup doesn't include. - external: (id) => - !id.startsWith(".") && - !id.startsWith("/") && - !id.startsWith("src/") && - !id.startsWith("@unflakable/plugins-common/") && - ![ - // Avoid having skip-tests depend on @unflakable/js-api, which could pull in Node dependencies - // that Webpack v5 doesn't bundle by default. It's also unclear which versions of Webpack - // support sub-path exports from package.json like this. To avoid requiring any changes to - // the user's Webpack config, we try to make skip-tests self-contained and easy to import. - "@unflakable/js-api/consts", - "@unflakable/plugins-common", - "widest-line", - ].includes(id), - plugins: [ - pluginCommonJs(), - pluginJson(), - pluginNodeResolve({ preferBuiltins: true }), - pluginTypescript({ tsconfig: "src/tsconfig.json" }), - ], - treeshake: { - // Assume internal modules do not have side effects when they're imported. This helps remove - // unnecessary require()'s from the transpiled code. - moduleSideEffects: (id, external) => external, + { + input: "src/config-wrapper.ts", + output: { + chunkFileNames: "[name]-[hash].mjs", + file: "dist/config-wrapper.mjs", + format: "es", + }, + external: isExternal, + plugins, + treeshake, }, -}; +]; From 3840dc5b0536ff5f4e3e00b19d117b9ac2ccc203 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Fri, 23 Jun 2023 17:22:15 -0700 Subject: [PATCH 02/12] Rollup exported TypeScript types Both @unflakable/cypress-plugin and @unflakable/jest-plugin include exported TypeScript types. Since both packages depend on the internal @unflakable/plugins-common package, we need to use rollup-plugin-dts to be sure that the exported types don't refer to this unpublished package. --- packages/cypress-plugin/package.json | 5 +- packages/cypress-plugin/rollup.config.mjs | 28 ++++++- packages/jest-plugin/package.json | 5 +- packages/jest-plugin/rollup.config.mjs | 96 +++++++++++++++-------- packages/jest-plugin/tsconfig.json | 3 +- yarn.lock | 1 + 6 files changed, 99 insertions(+), 39 deletions(-) diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index f1aa35c..7db2f55 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -35,9 +35,9 @@ "README.md", "dist/**/*.js", "dist/**/*.mjs", - "dist/index.d.ts", - "dist/config-wrapper.d.ts", "dist/config-wrapper-sync.d.ts", + "dist/config-wrapper.d.ts", + "dist/index.d.ts", "dist/reporter.d.ts", "dist/skip-tests.d.ts" ], @@ -83,6 +83,7 @@ "jest": "^29.5.0", "jest-environment-node": "^29.5.0", "rollup": "^3.21.1", + "rollup-plugin-dts": "^5.3.0", "typescript": "^4.9.5", "widest-line": "3.1.0" }, diff --git a/packages/cypress-plugin/rollup.config.mjs b/packages/cypress-plugin/rollup.config.mjs index 95dcabd..b1e732f 100644 --- a/packages/cypress-plugin/rollup.config.mjs +++ b/packages/cypress-plugin/rollup.config.mjs @@ -1,6 +1,7 @@ // Copyright (c) 2023 Developer Innovations, LLC import pluginCommonJs from "@rollup/plugin-commonjs"; +import pluginDts from "rollup-plugin-dts"; import pluginJson from "@rollup/plugin-json"; import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginTypescript from "@rollup/plugin-typescript"; @@ -8,7 +9,7 @@ import pluginTypescript from "@rollup/plugin-typescript"; /** * Bundle the internal @unflakable/plugins-common package, along with dependencies used by * vendored Cypress code that Cypress itself also bundles (i.e., doesn't list in its public - * package.json as deps) in dist/, but leave most other imported packages as an external. Internal + * package.json as deps) in dist/, but leave most other imported packages as external. Internal * modules begin with `.` or `/`. We don't include `term-size` here because it depends on a * bundled vendor/ directory that rollup doesn't include. * @@ -86,4 +87,29 @@ export default [ plugins, treeshake, }, + // Rollup types so that UnflakableConfig from @unflakable/plugins-common is bundled. This package + // doesn't get published separately, so our public types shouldn't reference it. + { + input: [ + // NB: This should include every exported .d.ts from package.json. + "dist/config-wrapper-sync.d.ts", + "dist/config-wrapper.d.ts", + "dist/index.d.ts", + "dist/reporter.d.ts", + "dist/skip-tests.d.ts", + ], + output: { + dir: ".", + entryFileNames: "[name].d.ts", + format: "cjs", + }, + external: isExternal, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + plugins: [ + pluginNodeResolve({ preferBuiltins: true }), + pluginDts({ + respectExternal: true, + }), + ], + }, ]; diff --git a/packages/jest-plugin/package.json b/packages/jest-plugin/package.json index 0c25483..02236e8 100644 --- a/packages/jest-plugin/package.json +++ b/packages/jest-plugin/package.json @@ -62,7 +62,8 @@ "jest-util": "25.1.0 - 29" }, "scripts": { - "build": "tsc --noEmit && rollup --config", - "build:watch": "rollup --config --watch" + "build": "yarn clean && tsc --noEmit && rollup --config", + "build:watch": "rollup --config --watch", + "clean": "rm -rf dist/" } } diff --git a/packages/jest-plugin/rollup.config.mjs b/packages/jest-plugin/rollup.config.mjs index d39da52..66c3880 100644 --- a/packages/jest-plugin/rollup.config.mjs +++ b/packages/jest-plugin/rollup.config.mjs @@ -1,41 +1,73 @@ // Copyright (c) 2023 Developer Innovations, LLC -import pluginTypescript from "@rollup/plugin-typescript"; -import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginCommonJs from "@rollup/plugin-commonjs"; +import pluginDts from "rollup-plugin-dts"; import pluginJson from "@rollup/plugin-json"; +import pluginNodeResolve from "@rollup/plugin-node-resolve"; +import pluginTypescript from "@rollup/plugin-typescript"; + +/** + * Bundle the internal @unflakable/plugins-common package, but leave most other imported packages + * as external. Internal modules begin with `.` or `/`. + * + * @type {import("rollup").IsExternal} + */ +const isExternal = (id) => + !id.startsWith(".") && + !id.startsWith("/") && + !id.startsWith("src/") && + !id.startsWith("@unflakable/plugins-common/") && + ![ + // Support older versions of Jest that don't support sub-path externals in package.json. + "@unflakable/js-api/consts", + "@unflakable/plugins-common", + ].includes(id); /** * @type {import("rollup").NormalizedInputOptions} */ -export default { - input: ["src/reporter.ts", "src/runner.ts"], - output: { - dir: "dist", - format: "cjs", - // Mimicks TypeScript `esModuleInterop` (see - // https://rollupjs.org/configuration-options/#output-format). - interop: "auto", - sourcemap: true, +export default [ + { + input: ["src/reporter.ts", "src/runner.ts"], + output: { + dir: "dist", + format: "cjs", + // Mimicks TypeScript `esModuleInterop` (see + // https://rollupjs.org/configuration-options/#output-format). + interop: "auto", + }, + // Bundle the internal @unflakable/plugins-common package in dist/, but leave most other + // imported packages as an external. Internal modules begin with `.` or `/`. + external: isExternal, + // Unclear why this is necessary. + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + plugins: [ + pluginCommonJs(), + pluginJson(), + pluginNodeResolve(), + pluginTypescript(), + ], + }, + // Rollup types so that UnflakableConfig from @unflakable/plugins-common is bundled. This package + // doesn't get published separately, so our public types shouldn't reference it. + { + input: [ + // NB: This should include every exported .d.ts from package.json. + "dist/reporter.d.ts", + "dist/runner.d.ts", + ], + output: { + dir: ".", + entryFileNames: "[name].d.ts", + format: "cjs", + }, + external: isExternal, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + plugins: [ + pluginNodeResolve({ preferBuiltins: true }), + pluginDts({ + respectExternal: true, + }), + ], }, - // Bundle the internal @unflakable/plugins-common package in dist/, but leave most other - // imported packages as an external. Internal modules begin with `.` or `/`. - external: (id) => - !id.startsWith(".") && - !id.startsWith("/") && - !id.startsWith("src/") && - !id.startsWith("@unflakable/plugins-common/") && - ![ - // Support older versions of Jest that don't support sub-path externals in package.json. - "@unflakable/js-api/consts", - "@unflakable/plugins-common", - ].includes(id), - // Unclear why this is necessary. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - plugins: [ - pluginCommonJs(), - pluginJson(), - pluginNodeResolve(), - pluginTypescript(), - ], -}; +]; diff --git a/packages/jest-plugin/tsconfig.json b/packages/jest-plugin/tsconfig.json index c2f62b5..d5ff9c4 100644 --- a/packages/jest-plugin/tsconfig.json +++ b/packages/jest-plugin/tsconfig.json @@ -12,8 +12,7 @@ // Avoids conflicting global definitions from, e.g., jasmine. "types": ["node", "jest"], // Some versions of Jest (e.g., 28.0.0) have internally broken types. - "skipLibCheck": true, - "sourceMap": true + "skipLibCheck": true }, "include": [ ".eslintrc.js", diff --git a/yarn.lock b/yarn.lock index d57fbcf..d418eb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2678,6 +2678,7 @@ __metadata: ms: 2.1.1 path-browserify: ^1.0.1 rollup: ^3.21.1 + rollup-plugin-dts: ^5.3.0 simple-git: ^3.16.0 term-size: 2.1.0 tmp: ~0.2.1 From fdcac51343a0358a8ea94db3c7957980005f9a44 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Sun, 25 Jun 2023 22:28:08 -0700 Subject: [PATCH 03/12] [cypress] Add Windows support --- .github/workflows/ci.yaml | 159 +++- packages/cypress-plugin/package.json | 4 +- packages/cypress-plugin/rollup.config.mjs | 5 +- .../cypress-plugin/src/load-user-config.ts | 4 +- packages/cypress-plugin/src/plugin.ts | 46 +- packages/cypress-plugin/src/reporter.ts | 27 +- packages/cypress-plugin/src/skip-tests.ts | 11 +- .../test/integration-common/package.json | 3 +- .../test/integration-common/rollup.config.mjs | 3 +- .../test/integration/package.json | 1 - .../test/integration/src/parse-output.ts | 28 +- .../test/integration/src/run-test-case.ts | 49 +- .../test/integration/src/test-wrappers.ts | 25 +- .../test/integration/src/verify-output.ts | 80 +- packages/jest-plugin/package.json | 3 +- packages/jest-plugin/rollup.config.mjs | 3 +- packages/js-api/package.json | 5 +- packages/plugins-common/package.json | 3 +- packages/plugins-common/src/index.ts | 7 + packages/plugins-common/src/quarantine.ts | 4 +- yarn.lock | 704 +++++++++++++++++- 21 files changed, 1040 insertions(+), 134 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8d33d0b..6c09c64 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -88,9 +88,8 @@ jobs: affects_cypress: ${{ steps.affects_plugins.outputs.cypress }} affects_jest: ${{ steps.affects_plugins.outputs.jest }} - cypress_integration_tests: - name: "Cypress Integration Tests: Cypress ${{ matrix.cypress }} + Node ${{ matrix.node }}" - # FIXME: also test on Windows + cypress_linux_integration_tests: + name: "Cypress ${{ matrix.cypress }} Linux Node ${{ matrix.node }} Integration Tests" runs-on: ubuntu-latest timeout-minutes: 60 needs: @@ -126,12 +125,38 @@ jobs: uses: actions/cache@v3 with: path: ~/.cache/Cypress - key: cypress-${{ runner.os }}-node${{ matrix.node }}-cypress${{ matrix.cypress }}-modules-${{ hashFiles('yarn.lock') }} + key: cypress-${{ runner.os }}-node${{ matrix.node }}-cypress${{ matrix.cypress }} - id: install + env: + CYPRESS_INSTALL_BINARY: "0" run: yarn install --immutable + - uses: actions/download-artifact@v3 + with: + path: .artifacts + + - name: Install pre-built plugin packages + env: + CYPRESS_INSTALL_BINARY: "0" + run: | + curl -Lo jq https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64 + chmod +x jq + cat package.json \ + | ./jq '. + {"resolutions": (.resolutions + { + "@unflakable/cypress-plugin": "file:./.artifacts/cypress-plugin/package.tgz", + "@unflakable/jest-plugin": "file:./.artifacts/jest-plugin/package.tgz", + "@unflakable/js-api": "file:./.artifacts/js-api/package.tgz" + })}' > package-new.json + mv package-new.json package.json + yarn install --no-immutable + + - name: Build test dependencies + run: yarn build:plugins-common && yarn build:cypress-tests + - name: Set Cypress version + env: + CYPRESS_INSTALL_BINARY: "0" run: | yarn set resolution "cypress@npm:10 - 12" ${{ matrix.cypress }} grep --after-context=1 "^\".*cypress.*" yarn.lock @@ -139,8 +164,6 @@ jobs: - name: Install Cypress binary run: yarn workspace cypress-integration exec cypress install - - run: yarn build:plugins && yarn build:cypress-tests - - name: Test env: # Enable debug logs within the Jest tests that run Cypress. WARNING: these are very @@ -163,11 +186,109 @@ jobs: fi UNFLAKABLE_API_KEY=${{ secrets.UNFLAKABLE_API_KEY }} \ yarn workspace cypress-integration test \ - --reporters @unflakable/jest-plugin/dist/reporter \ - --runner @unflakable/jest-plugin/dist/runner + --reporters @unflakable/jest-plugin/dist/reporter \ + --runner @unflakable/jest-plugin/dist/runner + + cypress_windows_integration_tests: + name: "Cypress ${{ matrix.cypress }} Windows Node ${{ matrix.node }} Integration Tests" + runs-on: windows-2022 + # Cypress on Windows is slowwww... + timeout-minutes: 90 + needs: + # Don't incur the cost of the test matrix if the basic build fails. + - check + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || needs.check.outputs.affects_cypress == 'true' + strategy: + fail-fast: false + matrix: + node: + - 16 + - 18 + - 20 + cypress: + # FIXME: support earlier versions + #- "10.0" + #- "10.11" + #- "11.0" + - "11.2" + - "12.0" + - "12.10" + - "12.14" + - "12.15" + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + cache: yarn + + - name: Get AppData directory + run: echo ("LOCALAPPDATA=" + $env:LOCALAPPDATA) >> $env:GITHUB_ENV + + - name: Cache Cypress binary + uses: actions/cache@v3 + with: + path: ${{ env.LOCALAPPDATA }}\Cypress\Cache + key: cypress-${{ runner.os }}-node${{ matrix.node }}-cypress${{ matrix.cypress }} + + - id: install + env: + CYPRESS_INSTALL_BINARY: "0" + run: | + yarn install --immutable + + - uses: actions/download-artifact@v3 + with: + path: .artifacts + + - name: Install pre-built plugin packages + env: + CYPRESS_INSTALL_BINARY: "0" + shell: bash + run: | + curl -Lo jq.exe https://github.com/jqlang/jq/releases/download/jq-1.6/jq-win64.exe + cat package.json \ + | ./jq.exe '. + {"resolutions": (.resolutions + { + "@unflakable/cypress-plugin": "file:./.artifacts/cypress-plugin/package.tgz", + "@unflakable/jest-plugin": "file:./.artifacts/jest-plugin/package.tgz", + "@unflakable/js-api": "file:./.artifacts/js-api/package.tgz" + })}' > package-new.json + mv package-new.json package.json + yarn install --no-immutable + + - name: Build test dependencies + run: yarn build:plugins-common && yarn build:cypress-tests + + - name: Set Cypress version + env: + CYPRESS_INSTALL_BINARY: "0" + run: | + yarn set resolution "cypress@npm:10 - 12" ${{ matrix.cypress }} + Select-String -Pattern '^".*cypress.*' -Path yarn.lock -Context 0,1 + + - name: Install Cypress binary + run: yarn workspace cypress-integration exec cypress install + + - name: Test + env: + # Enable debug logs within the Jest tests that run Cypress. WARNING: these are very + # verbose but are useful for seeing the raw chalk terminal codes. + # DEBUG: unflakable:* + + # Enable debug logs within the Cypress plugin. + TEST_DEBUG: unflakable:* + + # Enable terminal colors for debug() output. + DEBUG_COLORS: "1" + + # Make chalk emit TTY colors. + FORCE_COLOR: "1" + # FIXME: use Jest plugin once it works on Windows + run: yarn workspace cypress-integration test jest_integration_tests: - name: "Jest Integration Tests: Jest ${{ matrix.jest }} + Node ${{ matrix.node }}" + name: "Jest ${{ matrix.jest }} Linux Node ${{ matrix.node }} Integration Tests" # FIXME: also test on Windows runs-on: ubuntu-latest timeout-minutes: 20 @@ -209,6 +330,8 @@ jobs: - "25.3" - "25.2" - "25.1" + env: + CYPRESS_INSTALL_BINARY: "0" steps: - uses: actions/checkout@v3 @@ -221,6 +344,22 @@ jobs: - id: install run: yarn install --immutable + - uses: actions/download-artifact@v3 + with: + path: .artifacts + + - name: Install pre-built plugin packages + run: | + curl -Lo jq https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64 + chmod +x jq + cat package.json \ + | ./jq '. + {"resolutions": (.resolutions + { + "@unflakable/jest-plugin": "file:./.artifacts/jest-plugin/package.tgz", + "@unflakable/js-api": "file:./.artifacts/js-api/package.tgz" + })}' > package-new.json + mv package-new.json package.json + yarn install --no-immutable + # Since Jest is composed of many packages, we need to make sure that they're all using the # same major and minor versions. Otherwise, NPM will keep the newer minor versions since # they're semver-compatible. This script iteratively installs the lowest compatible semver @@ -235,8 +374,6 @@ jobs: if: ${{ startsWith(matrix.jest, '25.') }} run: yarn set resolution "chalk@npm:^3.0.0 || ^4.0.0" 3.0 - - run: yarn build:plugins - - name: Test env: DEBUG: unflakable:* diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index 7db2f55..d0f0bed 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -45,6 +45,7 @@ "cypress-unflakable": "./dist/main.js" }, "dependencies": { + "@stdlib/utils-convert-path": "^0.0.8", "@unflakable/js-api": "workspace:^", "ansi-styles": "^4.3.0", "chalk": "^4.1.0", @@ -82,6 +83,7 @@ "cypress": "10 - 12", "jest": "^29.5.0", "jest-environment-node": "^29.5.0", + "rimraf": "^5.0.1", "rollup": "^3.21.1", "rollup-plugin-dts": "^5.3.0", "typescript": "^4.9.5", @@ -92,7 +94,7 @@ }, "scripts": { "build": "yarn clean && tsc --noEmit && tsc --noEmit -p src && rollup --config", - "clean": "rm -rf dist/", + "clean": "rimraf dist/", "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --useStderr --verbose" } } diff --git a/packages/cypress-plugin/rollup.config.mjs b/packages/cypress-plugin/rollup.config.mjs index b1e732f..f7b899a 100644 --- a/packages/cypress-plugin/rollup.config.mjs +++ b/packages/cypress-plugin/rollup.config.mjs @@ -5,6 +5,7 @@ import pluginDts from "rollup-plugin-dts"; import pluginJson from "@rollup/plugin-json"; import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginTypescript from "@rollup/plugin-typescript"; +import path from "path"; /** * Bundle the internal @unflakable/plugins-common package, along with dependencies used by @@ -17,8 +18,8 @@ import pluginTypescript from "@rollup/plugin-typescript"; */ const isExternal = (id) => !id.startsWith(".") && - !id.startsWith("/") && - !id.startsWith("src/") && + !path.isAbsolute(id) && + !id.startsWith(`src/`) && !id.startsWith("@unflakable/plugins-common/") && ![ // Avoid having skip-tests depend on @unflakable/js-api, which could pull in Node dependencies diff --git a/packages/cypress-plugin/src/load-user-config.ts b/packages/cypress-plugin/src/load-user-config.ts index aee923e..48a26d1 100644 --- a/packages/cypress-plugin/src/load-user-config.ts +++ b/packages/cypress-plugin/src/load-user-config.ts @@ -7,6 +7,7 @@ import { ENV_VAR_USER_CONFIG_PATH, } from "./config-env-vars"; import path from "path"; +import { pathToFileURL } from "url"; const debug = _debug("unflakable:load-user-config"); @@ -55,7 +56,8 @@ export const loadUserConfig = async (): Promise< // for ESM projects. debug(`require() failed; attempting dynamic import(): ${e as string}`); const config = (await import( - ENV_VAR_USER_CONFIG_PATH.value as string + // Windows paths don't work unless we convert them to file:// URLs. + pathToFileURL(ENV_VAR_USER_CONFIG_PATH.value as string).href )) as LoadedConfig; return config.default ?? config; } diff --git a/packages/cypress-plugin/src/plugin.ts b/packages/cypress-plugin/src/plugin.ts index d5beec6..ad9b93a 100644 --- a/packages/cypress-plugin/src/plugin.ts +++ b/packages/cypress-plugin/src/plugin.ts @@ -16,9 +16,10 @@ import { commitOverride, isTestQuarantined, normalizeTestName, + toPosix, UnflakableConfig, } from "@unflakable/plugins-common"; -import { require, userAgent } from "./utils"; +import { printWarning, require, userAgent } from "./utils"; import { configureMochaReporter } from "./reporter-config"; import { color, @@ -393,7 +394,7 @@ export class UnflakableCypressPlugin { options.autoSupportFile !== false ) { const updatedSupportFile = await this.generateSkipTestsSupportFile( - config.supportFile + config ); this.supportFilePath = updatedSupportFile; config.supportFile = updatedSupportFile; @@ -411,32 +412,37 @@ export class UnflakableCypressPlugin { }; private generateSkipTestsSupportFile = async ( - configuredSupportFile: string | false + config: Cypress.PluginConfigOptions ): Promise => { const debug = baseDebug.extend("generateSkipTestsSupportFile"); // We have to write a temporary Cypress support file on the fly because Cypress can't load // support files inside of node_modules. See https://github.com/cypress-io/cypress/issues/23616. // Once Cypress loads our support file, it's fine to load other modules that live in - // node_modules. + // node_modules. Cypress also requires the support file to be located within the project root + // due to https://github.com/cypress-io/cypress/issues/8599#issuecomment-1290526416. + const tmpdir = path.join(config.projectRoot, ".unflakable-tmp"); + await fs.mkdir(tmpdir, { recursive: true }); + const supportFilePath = (await promisify(tmpName)({ - prefix: "unflakable-cypress-support-file", - })) + ".js"; + prefix: "cypress-support-file", + tmpdir, + })) + ".cjs"; debug(`Using temp path \`${supportFilePath}\` for Cypress support file`); const skipTestsPath = require.resolve(SKIP_TESTS_MODULE); debug(`Support file will load skip-tests from ${skipTestsPath}`); - if (configuredSupportFile !== false) { - debug(`Will load existing support file from ${configuredSupportFile}`); + if (config.supportFile !== false) { + debug(`Will load existing support file from ${config.supportFile}`); } const supportFileContents = ` require(${JSON.stringify(skipTestsPath)}).registerMochaInstrumentation(); ${ - configuredSupportFile !== false - ? `require(${JSON.stringify(configuredSupportFile)});` + config.supportFile !== false + ? `require(${JSON.stringify(config.supportFile)});` : "" } `; @@ -560,6 +566,17 @@ ${ const debug = baseDebug.extend("afterRun"); debug("Received afterRun event"); + if (this.supportFilePath !== null) { + debug(`Deleting temp support file ${this.supportFilePath}`); + await fs.unlink(this.supportFilePath).catch((e) => { + printWarning( + `Failed to delete temp support file ${ + this.supportFilePath as string + }: ${e as string}` + ); + }); + } + if (results.status === "finished") { renderSummaryTable(results); @@ -568,7 +585,9 @@ ${ // compile (e.g., due to Webpack errors). (tests ?? []) .map((test): TestRunRecord => { - const filename = path.relative(this.repoRoot, spec.absolute); + const filename = toPosix( + path.relative(this.repoRoot, spec.absolute) + ); const isQuarantined = this.manifest !== null && this.unflakableConfig.quarantineMode !== "no_quarantine" && @@ -597,11 +616,6 @@ ${ await this.uploadResults(results, testRuns); } } - - if (this.supportFilePath !== null) { - debug(`Deleting temp support file ${this.supportFilePath}`); - await fs.unlink(this.supportFilePath); - } }; private onBeforeSpec = (spec: Cypress.Spec): void => { diff --git a/packages/cypress-plugin/src/reporter.ts b/packages/cypress-plugin/src/reporter.ts index 5e24966..8e3551a 100644 --- a/packages/cypress-plugin/src/reporter.ts +++ b/packages/cypress-plugin/src/reporter.ts @@ -13,6 +13,7 @@ import { import { TestSuiteManifest } from "@unflakable/js-api"; import { isTestQuarantined, + toPosix, UnflakableConfig, } from "@unflakable/plugins-common"; import path from "path"; @@ -277,7 +278,7 @@ export default class UnflakableSpecReporter extends reporters.Base { private readonly config: UnflakableConfig; private readonly manifest: TestSuiteManifest | null; - private readonly testFilename: string | null; + private readonly posixTestFilename: string | null; private indents = 0; @@ -316,17 +317,19 @@ export default class UnflakableSpecReporter extends reporters.Base { options.reporterOptions as ReporterConfig; this.config = config; this.manifest = manifest ?? null; - this.testFilename = + this.posixTestFilename = runner.suite.file !== undefined - ? path.relative( - repoRoot, - // Absolute path of the spec file. - path.join(projectRoot, runner.suite.file) + ? toPosix( + path.relative( + repoRoot, + // Absolute path of the spec file. + path.join(projectRoot, runner.suite.file) + ) ) : // This can happen if a file has no tests, which should be ok since none of the event // handlers should get called. null; - debug(`Repo-relative test filename: ${this.testFilename ?? "null"}`); + debug(`Repo-relative test filename: ${this.posixTestFilename ?? "null"}`); runner.on(Runner.constants.EVENT_RUN_BEGIN, this.onRunBegin.bind(this)); runner.once(Runner.constants.EVENT_RUN_END, this.onRunEnd.bind(this)); @@ -365,18 +368,18 @@ export default class UnflakableSpecReporter extends reporters.Base { return false; } - if (this.testFilename === null) { + if (this.posixTestFilename === null) { throw new Error("Suite has no `file` attribute"); } const isQuarantined = this.manifest !== null && - isTestQuarantined(this.manifest, this.testFilename, titlePath); + isTestQuarantined(this.manifest, this.posixTestFilename, titlePath); debug( `Test is ${isQuarantined ? "" : "NOT "}quarantined: ${JSON.stringify( titlePath - )} in file ${this.testFilename}` + )} in file ${this.posixTestFilename}` ); return isQuarantined; @@ -845,12 +848,12 @@ export default class UnflakableSpecReporter extends reporters.Base { // warning in this case rather than treating the test as flaky or potentially quarantining // it. if (currentTestRetry(test) > 0) { - if (this.testFilename === null) { + if (this.posixTestFilename === null) { throw new Error("Suite has no `file` attribute"); } printWarning( - `test ${titlePathJson} in file ${this.testFilename} was pending (skipped) during retry` + `test ${titlePathJson} in file ${this.posixTestFilename} was pending (skipped) during retry` ); } diff --git a/packages/cypress-plugin/src/skip-tests.ts b/packages/cypress-plugin/src/skip-tests.ts index 01ac7f5..18531b2 100644 --- a/packages/cypress-plugin/src/skip-tests.ts +++ b/packages/cypress-plugin/src/skip-tests.ts @@ -27,6 +27,7 @@ import { CYPRESS_ENV_VAR_MANIFEST, CYPRESS_ENV_VAR_REPO_ROOT, } from "./cypress-env-vars"; +import convertPath from "@stdlib/utils-convert-path"; const debug = _debug("unflakable:skip-tests"); @@ -110,7 +111,15 @@ const instrumentTestFn = ( config: Cypress.TestConfigOverrides | undefined, fn: Mocha.Func | undefined ): Mocha.Test => { - const testFilename = path.relative(repoRoot, Cypress.spec.absolute); + // NB: On Windows, Cypress.spec.absolute uses a mixed path convention (e.g., C:/foo/bar), + // while repoRoot uses a Windows path convention (e.g., C:\foo\bar). In order for + // path.relative to work correctly, we have to convert both to POSIX conventions (e.g., + // /c/foo/bar). This is because path-browserify doesn't have win32 support: + // https://github.com/browserify/path-browserify/issues/1. + const testFilename = path.relative( + convertPath(repoRoot, "posix"), + convertPath(Cypress.spec.absolute, "posix") + ); const titlePath = [...suiteStack, title]; const isQuarantined = manifest !== null && diff --git a/packages/cypress-plugin/test/integration-common/package.json b/packages/cypress-plugin/test/integration-common/package.json index ea472b5..a852526 100644 --- a/packages/cypress-plugin/test/integration-common/package.json +++ b/packages/cypress-plugin/test/integration-common/package.json @@ -11,12 +11,13 @@ "@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-typescript": "^11.1.1", "@unflakable/plugins-common": "workspace:^", + "rimraf": "^5.0.1", "rollup": "^3.21.1", "rollup-plugin-dts": "^5.3.0", "typescript": "^4.9.5" }, "scripts": { "build": "yarn clean && tsc --noEmit && tsc --noEmit -p src && rollup --config", - "clean": "rm -rf dist/" + "clean": "rimraf dist/" } } diff --git a/packages/cypress-plugin/test/integration-common/rollup.config.mjs b/packages/cypress-plugin/test/integration-common/rollup.config.mjs index c7801cf..1474adc 100644 --- a/packages/cypress-plugin/test/integration-common/rollup.config.mjs +++ b/packages/cypress-plugin/test/integration-common/rollup.config.mjs @@ -1,5 +1,6 @@ // Copyright (c) 2023 Developer Innovations, LLC +import path from "path"; import pluginCommonJs from "@rollup/plugin-commonjs"; import pluginNodeResolve from "@rollup/plugin-node-resolve"; import pluginTypescript from "@rollup/plugin-typescript"; @@ -14,7 +15,7 @@ import pluginDts from "rollup-plugin-dts"; */ const isExternal = (id) => !id.startsWith(".") && - !id.startsWith("/") && + !path.isAbsolute(id) && !id.startsWith("src/") && !["@unflakable/plugins-common"].includes(id); diff --git a/packages/cypress-plugin/test/integration/package.json b/packages/cypress-plugin/test/integration/package.json index 43c3ea0..15c65f1 100644 --- a/packages/cypress-plugin/test/integration/package.json +++ b/packages/cypress-plugin/test/integration/package.json @@ -3,7 +3,6 @@ "private": true, "devDependencies": { "@types/jest": "^29.5.2", - "@unflakable/cypress-plugin": "workspace:^", "@unflakable/jest-plugin": "workspace:^", "@unflakable/js-api": "workspace:^", "@unflakable/plugins-common": "workspace:^", diff --git a/packages/cypress-plugin/test/integration/src/parse-output.ts b/packages/cypress-plugin/test/integration/src/parse-output.ts index d8e31f9..a2cb307 100644 --- a/packages/cypress-plugin/test/integration/src/parse-output.ts +++ b/packages/cypress-plugin/test/integration/src/parse-output.ts @@ -2,11 +2,7 @@ /* eslint-disable no-control-regex */ -import { - specProjectPath, - TEST_SPEC_STUBS, - TestCaseParams, -} from "./run-test-case"; +import { specPattern, TEST_SPEC_STUBS, TestCaseParams } from "./run-test-case"; export type RunStarting = { specs: string[]; @@ -185,8 +181,6 @@ const parseRunStarting = ( linesRead: number; runStarting: RunStarting; } => { - const { specNameStubs, testMode } = params; - const runStartingLine = stdoutLines.findIndex( (line) => line === "\x1B[0m (\x1B[4m\x1B[1mRun Starting\x1B[22m\x1B[24m)\x1B[0m" @@ -213,7 +207,9 @@ const parseRunStarting = ( expect(tableEntries["Cypress"]).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); expect(tableEntries["Browser"]).toMatch( - /^Chrome [0-9]+ \x1B\[90m\(headless\)\x1B\[39m$/ + // Electron is much faster on Windows than Chrome, and only a tiny bit slower on Linux, so we + // just use it for all the integration tests. + /^Electron [0-9]+ \x1B\[90m\(headless\)\x1B\[39m$/ ); expect(tableEntries["Node Version"]).toMatch( new RegExp( @@ -224,13 +220,7 @@ const parseRunStarting = ( )} \\x1B\\[90m\\(.+\\)\\x1B\\[39m$` ) ); - expect(tableEntries["Searched"]).toBe( - specNameStubs !== undefined && specNameStubs.length > 0 - ? specNameStubs.map((stub) => specProjectPath(params, stub)).join(", ") - : testMode === "component" - ? "**/*.cy.{js,jsx,ts,tsx}" - : `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}` - ); + expect(tableEntries["Searched"]).toBe(specPattern(params)); const parsedSpecsLine = tableEntries["Specs"].match( /^([0-9]+) found \((.*)\)$/ @@ -942,7 +932,7 @@ const parseSummaryTable = ( .filter((line) => !TABLE_BETWEEN_ROWS_BORDER_LINE.test(line)) .map((line): SummaryRow => { const parsedRowOrNull = line.match( - /^\x1B\[90m +│\x1B\[39m \x1B\[3(?:2m(?✔)|1m(?✖))\x1B\[39m +\x1B\[0m(?.+?)\x1B\[0m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)\x1B\[0m|90m-\x1B\[39m) +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:33m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:35m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m│\x1B\[39m$/ + /^\x1B\[90m +│\x1B\[39m \x1B\[3(?:2m(?[✔√])|1m(?[✖×]))\x1B\[39m +\x1B\[0m(?.+?)\x1B\[0m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)\x1B\[0m|90m-\x1B\[39m) +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:33m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:35m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m│\x1B\[39m$/ ); expect( @@ -991,7 +981,7 @@ const parseSummaryTable = ( const summaryTotalsLine = stdoutLinesAfterLastSpecTable[runFinishedLine + 5 + numTableLines + 1]; const parsedSummaryTotalsOrNull = summaryTotalsLine.match( - /^\x1B\[90m +\x1B\[39m \x1B\[3(?:2m(?✔)|1m(?✖))\x1B\[39m +\x1B\[3(:?2mAll specs passed!|1m(?[0-9]+) of (?[0-9]+) failed \([0-9]+%\))\x1B\[39m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)|90m-)\x1B\[0m +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:33m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:35m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m \x1B\[39m$/ + /^\x1B\[90m +\x1B\[39m \x1B\[3(?:2m(?[✔√])|1m(?[✖×]))\x1B\[39m +\x1B\[3(:?2mAll specs passed!|1m(?[0-9]+) of (?[0-9]+) failed \([0-9]+%\))\x1B\[39m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)|90m-)\x1B\[0m +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:33m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:35m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m \x1B\[39m$/ ); expect( parsedSummaryTotalsOrNull, @@ -1074,7 +1064,7 @@ const parsePluginDisabledSummaryTable = ( .filter((line) => !TABLE_BETWEEN_ROWS_BORDER_LINE.test(line)) .map((line): SummaryRow => { const parsedRowOrNull = line.match( - /^\x1B\[90m +│\x1B\[39m \x1B\[3(?:2m(?✔)|1m(?✖))\x1B\[39m +\x1B\[0m(?.+?)\x1B\[0m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)\x1B\[0m|90m-\x1B\[39m) +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m│\x1B\[39m$/ + /^\x1B\[90m +│\x1B\[39m \x1B\[3(?:2m(?[✔√])|1m(?[✖×]))\x1B\[39m +\x1B\[0m(?.+?)\x1B\[0m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)\x1B\[0m|90m-\x1B\[39m) +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:[39]4m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m│\x1B\[39m$/ ); expect( @@ -1116,7 +1106,7 @@ const parsePluginDisabledSummaryTable = ( const summaryTotalsLine = stdoutLinesAfterLastSpecTable[runFinishedLine + 5 + numTableLines + 1]; const parsedSummaryTotalsOrNull = summaryTotalsLine.match( - /^\x1B\[90m +\x1B\[39m \x1B\[3(?:2m(?✔)|1m(?✖))\x1B\[39m +\x1B\[3(:?2mAll specs passed!|1m(?[0-9]+) of (?[0-9]+) failed \([0-9]+%\))\x1B\[39m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)|90m-)\x1B\[0m +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:34m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m \x1B\[39m$/ + /^\x1B\[90m +\x1B\[39m \x1B\[3(?:2m(?[✔√])|1m(?[✖×]))\x1B\[39m +\x1B\[3(:?2mAll specs passed!|1m(?[0-9]+) of (?[0-9]+) failed \([0-9]+%\))\x1B\[39m +\x1B\[90m(?.+?)\x1B\[39m +\x1B\[(?:0m(?[0-9]+)|90m-)\x1B\[0m +\x1B\[(?:32m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:31m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:36m(?[0-9]+)|90m-)\x1B\[39m +\x1B\[(?:[39]4m(?[0-9]+)|90m-)\x1B\[39m \x1B\[90m \x1B\[39m$/ ); expect( parsedSummaryTotalsOrNull, diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index e5ab45a..4422667 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -132,6 +132,9 @@ export type TestCaseParams = { testMode: TestMode; }; +export const projectPath = (params: TestCaseParams): string => + path.join("..", params.project); + export const specFilename = ( params: TestCaseParams, specNameStub: string @@ -145,6 +148,27 @@ export const specProjectPath = ( specNameStub: string ): string => `cypress/${params.testMode}/${specFilename(params, specNameStub)}`; +export const specPattern = (params: TestCaseParams): string => { + const { specNameStubs, testMode } = params; + return specNameStubs !== undefined && specNameStubs.length > 0 + ? // Cypress doesn't convert to relative path on Windows due to hard-coding a forward slash + // into the path. See: + // https://github.com/cypress-io/cypress/blob/3d0a2b406115db292130df774348c4f1fd4a3240/packages/server/lib/modes/run.ts#L52 + specNameStubs + .map((stub) => + process.platform === "win32" + ? `${path.resolve(projectPath(params))}\\${specProjectPath( + params, + stub + ).replaceAll("/", "\\")}` + : specProjectPath(params, stub) + ) + .join(", ") + : testMode === "component" + ? "**/*.cy.{js,jsx,ts,tsx}" + : `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}`; +}; + const specRepoPath = (params: TestCaseParams, specNameStub: string): string => params.expectedRepoRelativePathPrefix + specProjectPath(params, specNameStub); @@ -757,9 +781,8 @@ export const runTestCase = async ( } }); - const projectPath = path.join("..", params.project); const configMockParams: CosmiconfigMockParams = { - searchFrom: path.resolve(projectPath), + searchFrom: path.resolve(projectPath(params)), searchResult: params.config !== null ? { @@ -773,7 +796,11 @@ export const runTestCase = async ( // in order to mock cosmiconfig for testing. Instead, we resolve the binary to an absolute path // using `yarn bin` and then invoke node directly. const cypressPluginBin = ( - await promisify(execFile)("yarn", ["bin", "cypress-unflakable"]) + await promisify(execFile)("yarn", ["bin", "cypress-unflakable"], { + cwd: projectPath(params), + // yarn.CMD isn't executable without a shell on Windows. + shell: process.platform === "win32", + }) ).stdout.trimEnd(); Object.entries(params.testEnvVars).forEach(([key, value]) => { @@ -808,9 +835,6 @@ export const runTestCase = async ( "--", // e2e/component `--${params.testMode}`, - // Chrome is faster than Electron, at least on Mac. - "--browser", - "chrome", ...(params.specNameStubs !== undefined ? [ "--spec", @@ -838,17 +862,26 @@ export const runTestCase = async ( UNFLAKABLE_API_BASE_URL: `http://localhost:${apiServer.port}`, [CONFIG_MOCK_ENV_VAR]: JSON.stringify(configMockParams), [GIT_MOCK_ENV_VAR]: JSON.stringify(params.git), + // Windows requires these environment variables to be propagated. + ...(process.platform === "win32" + ? { + APPDATA: process.env.APPDATA, + LOCALAPPDATA: process.env.LOCALAPPDATA, + TMP: process.env.TMP, + TEMP: process.env.TEMP, + } + : {}), }; debug( `Spawning test:\n args = %o\n environment = %o\n cwd = %s`, args, env, - projectPath + projectPath(params) ); const cypressChild = spawn("node", args, { - cwd: projectPath, + cwd: projectPath(params), env, }); diff --git a/packages/cypress-plugin/test/integration/src/test-wrappers.ts b/packages/cypress-plugin/test/integration/src/test-wrappers.ts index 7f94fc2..f3697b7 100644 --- a/packages/cypress-plugin/test/integration/src/test-wrappers.ts +++ b/packages/cypress-plugin/test/integration/src/test-wrappers.ts @@ -10,6 +10,7 @@ import path from "path"; import _debug from "debug"; import cypressPackage from "cypress/package.json"; import { SummaryTotals } from "./parse-output"; +import * as os from "os"; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -142,11 +143,23 @@ export const integrationTestSuite = (runTests: () => void): void => { ? cypressMinorVersion[0] : cypressPackage.version }`, () => { - // Only use Node major version for test name. - describe(`Node ${ - nodeMajorVersion !== null ? nodeMajorVersion[0] : process.version - }`, () => { - runTests(); - }); + const platform = os.platform(); + describe( + platform === "darwin" + ? `MacOS` + : platform === "linux" + ? "Linux" + : platform === "win32" + ? "Windows" + : platform, + () => { + // Only use Node major version for test name. + describe(`Node ${ + nodeMajorVersion !== null ? nodeMajorVersion[0] : process.version + }`, () => { + runTests(); + }); + } + ); }); }; diff --git a/packages/cypress-plugin/test/integration/src/verify-output.ts b/packages/cypress-plugin/test/integration/src/verify-output.ts index ec5b440..8a0b6fc 100644 --- a/packages/cypress-plugin/test/integration/src/verify-output.ts +++ b/packages/cypress-plugin/test/integration/src/verify-output.ts @@ -24,6 +24,8 @@ import escapeStringRegexp from "escape-string-regexp"; import { TestAttemptResult } from "@unflakable/js-api"; const THROWN_ERROR = "\x1B[0m\x1B[31m Error\x1B[0m\x1B[90m"; +const FAIL_SYMBOL = process.platform === "win32" ? "×" : "✖"; +const PASS_SYMBOL = process.platform === "win32" ? "√" : "✓"; const verifySpecOutput = ( params: TestCaseParams, @@ -119,11 +121,11 @@ const verifySpecOutputs = ( // Last retry has different output. { length: expectedRetries }, (_, idx) => - ` \x1B[31m ✖ mixed: failure should be quarantined\x1B[0m\x1B[33m (attempt ${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: failure should be quarantined\x1B[0m\x1B[33m (attempt ${ idx + 1 } of ${expectedRetries + 1})\x1B[0m` ), - ` \x1B[35m ✖ mixed: failure should be quarantined [failed, quarantined]\x1B[39m${ + ` \x1B[35m ${FAIL_SYMBOL} mixed: failure should be quarantined [failed, quarantined]\x1B[39m${ expectedRetries > 0 ? `\x1B[33m (attempt ${expectedRetries + 1} of ${ expectedRetries + 1 @@ -132,27 +134,27 @@ const verifySpecOutputs = ( }`, ...(expectedRetries > 0 ? [ - ` \x1B[31m ✖ mixed: flake should be quarantined\x1B[0m\x1B[33m (attempt 1 of ${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: flake should be quarantined\x1B[0m\x1B[33m (attempt 1 of ${ expectedRetries + 1 })\x1B[0m`, expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^ {2}\x1B\\[35m {2}✓\x1B\\[39m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ + `^ {2}\x1B\\[35m {2}${PASS_SYMBOL}\x1B\\[39m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] : [ - " \x1B[35m ✖ mixed: flake should be quarantined [failed, quarantined]\x1B[39m", + ` \x1B[35m ${FAIL_SYMBOL} mixed: flake should be quarantined [failed, quarantined]\x1B[39m`, ]), ] : [ ...Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ mixed: failure should be quarantined\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: failure should be quarantined\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -160,13 +162,13 @@ const verifySpecOutputs = ( : "" }` ), - ` \x1B[31m ✖ mixed: flake should be quarantined\x1B[0m\x1B[33m (attempt 1 of ${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: flake should be quarantined\x1B[0m\x1B[33m (attempt 1 of ${ expectedRetries + 1 })\x1B[0m`, expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^ {2}\x1B\\[33m {2}✓\x1B\\[0m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ + `^ {2}\x1B\\[33m {2}${PASS_SYMBOL}\x1B\\[0m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` ) @@ -180,7 +182,7 @@ const verifySpecOutputs = ( ? Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ mixed: should fail\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: should fail\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -198,14 +200,14 @@ const verifySpecOutputs = ( ] : expectedRetries > 0 ? [ - ` \x1B[31m ✖ mixed: should be flaky\x1B[0m\x1B[33m (attempt 1 of ${ + ` \x1B[31m ${FAIL_SYMBOL} mixed: should be flaky\x1B[0m\x1B[33m (attempt 1 of ${ expectedRetries + 1 })\x1B[0m`, quarantineFlake && expectQuarantinedTestsToBeQuarantined ? expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^ {2}\x1B\\[35m {2}✓\x1B\\[39m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ + `^ {2}\x1B\\[35m {2}${PASS_SYMBOL}\x1B\\[39m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` ) @@ -213,17 +215,19 @@ const verifySpecOutputs = ( : expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^ {2}\x1B\\[33m {2}✓\x1B\\[0m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ + `^ {2}\x1B\\[33m {2}${PASS_SYMBOL}\x1B\\[0m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] - : [" \x1B[31m ✖ mixed: should be flaky\x1B[0m"] + : [` \x1B[31m ${FAIL_SYMBOL} mixed: should be flaky\x1B[0m`] : [" \x1B[36m - mixed: should be flaky\x1B[0m"]), expectExt.stringMatching( // eslint-disable-next-line no-control-regex - /^ {2}\x1B\[32m {2}✓\x1B\[0m\x1B\[90m mixed: should pass\x1B\[0m\x1B\[90m \([0-9]+.+?\)\x1B\[0m$/ + new RegExp( + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m mixed: should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + ) ), " \x1B[36m - mixed: should be skipped\x1B[0m", ], @@ -599,7 +603,7 @@ const verifySpecOutputs = ( ? Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ should fail\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} should fail\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -612,7 +616,7 @@ const verifySpecOutputs = ( ? Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ should fail with multiple exceptions\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} should fail with multiple exceptions\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -626,7 +630,7 @@ const verifySpecOutputs = ( ? Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ should showDiff\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} should showDiff\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -694,14 +698,14 @@ const verifySpecOutputs = ( !expectQuarantinedTestsToBeSkipped) ? expectedRetries > 0 ? [ - `\x1B[31m ✖ should be flaky${expectedFlakeTestNameSuffix}\x1B[0m\x1B[33m (attempt 1 of ${ + `\x1B[31m ${FAIL_SYMBOL} should be flaky${expectedFlakeTestNameSuffix}\x1B[0m\x1B[33m (attempt 1 of ${ expectedRetries + 1 })\x1B[0m`, quarantineFlake && expectQuarantinedTestsToBeQuarantined ? expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^\x1B\\[35m {2}✓\x1B\\[39m\x1B\\[90m should be flaky${escapeStringRegexp( + `^\x1B\\[35m {2}${PASS_SYMBOL}\x1B\\[39m\x1B\\[90m should be flaky${escapeStringRegexp( expectedFlakeTestNameSuffix )}\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 @@ -711,7 +715,7 @@ const verifySpecOutputs = ( : expectExt.stringMatching( new RegExp( // eslint-disable-next-line no-control-regex - `^\x1B\\[33m {2}✓\x1B\\[0m\x1B\\[90m should be flaky${escapeStringRegexp( + `^\x1B\\[33m {2}${PASS_SYMBOL}\x1B\\[0m\x1B\\[90m should be flaky${escapeStringRegexp( expectedFlakeTestNameSuffix )}\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 @@ -720,7 +724,7 @@ const verifySpecOutputs = ( ), ] : [ - `\x1B[31m ✖ should be flaky${expectedFlakeTestNameSuffix}\x1B[0m`, + `\x1B[31m ${FAIL_SYMBOL} should be flaky${expectedFlakeTestNameSuffix}\x1B[0m`, ] : [ `\x1B[36m - should be flaky${expectedFlakeTestNameSuffix}\x1B[0m${ @@ -938,7 +942,9 @@ const verifySpecOutputs = ( ? [ expectExt.stringMatching( // eslint-disable-next-line no-control-regex - /^ {2}\x1B\[32m {2}✓\x1B\[0m\x1B\[90m should fail due to hook\x1B\[0m\x1B\[90m \([0-9]+.+?\)\x1B\[0m$/ + new RegExp( + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should fail due to hook\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + ) ), ] : Array.from( @@ -961,8 +967,8 @@ const verifySpecOutputs = ( // https://github.com/mochajs/mocha/blob/0be3f78491bbbcdc4dcea660ee7bfd557a225d9c/lib/runner.js#L332 (hookFailResult === "quarantined" && (!skipBeforeHook || idx === expectedRetries) - ? " \x1B[35m ✖ should fail due to hook [failed, quarantined]\x1B[39m" - : " \x1B[31m ✖ should fail due to hook\x1B[0m") + + ? ` \x1B[35m ${FAIL_SYMBOL} should fail due to hook [failed, quarantined]\x1B[39m` + : ` \x1B[31m ${FAIL_SYMBOL} should fail due to hook\x1B[0m`) + (skipBeforeHook && expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -973,13 +979,17 @@ const verifySpecOutputs = ( ? [ expectExt.stringMatching( // eslint-disable-next-line no-control-regex - /^ {2}\x1B\[32m {2}✓\x1B\[0m\x1B\[90m should be skipped\x1B\[0m\x1B\[90m \([0-9]+.+?\)\x1B\[0m$/ + new RegExp( + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should be skipped\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + ) ), ] : hookSkipResult === "fail" - ? [" \x1B[31m ✖ should be skipped\x1B[0m"] + ? [` \x1B[31m ${FAIL_SYMBOL} should be skipped\x1B[0m`] : hookSkipResult === "quarantined" - ? [" \x1B[35m ✖ should be skipped [failed, quarantined]\x1B[39m"] + ? [ + ` \x1B[35m ${FAIL_SYMBOL} should be skipped [failed, quarantined]\x1B[39m`, + ] : []), ], passing: @@ -1048,7 +1058,7 @@ const verifySpecOutputs = ( ? Array.from( { length: expectedRetries + 1 }, (_, idx) => - `\x1B[31m ✖ An uncaught error was detected outside of a test\x1B[0m${ + `\x1B[31m ${FAIL_SYMBOL} An uncaught error was detected outside of a test\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -1091,12 +1101,16 @@ const verifySpecOutputs = ( "called consoleLog command", expectExt.stringMatching( // eslint-disable-next-line no-control-regex - /^\x1B\[32m +✓\x1B\[0m\x1B\[90m should pass\x1B\[0m\x1B\[90m \([0-9]+.+?\)\x1B\[0m$/ + new RegExp( + `^\\x1B\\[32m +${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + ) ), "\x1B[0m suite name\x1B[0m", expectExt.stringMatching( // eslint-disable-next-line no-control-regex - /^ {2}\x1B\[32m {2}✓\x1B\[0m\x1B\[90m suite test should pass\x1B\[0m\x1B\[90m \([0-9]+.+?\)\x1B\[0m$/ + new RegExp( + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m suite test should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + ) ), ], passing: 2, @@ -1193,7 +1207,7 @@ const verifySpecOutputs = ( // Last retry has different output. { length: expectedRetries }, (_, idx) => - ` \x1B[31m ✖ should be quarantined\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} should be quarantined\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 @@ -1201,7 +1215,7 @@ const verifySpecOutputs = ( : "" }` ), - ` \x1B[35m ✖ should be quarantined [failed, quarantined]\x1B[39m${ + ` \x1B[35m ${FAIL_SYMBOL} should be quarantined [failed, quarantined]\x1B[39m${ expectedRetries > 0 ? `\x1B[33m (attempt ${expectedRetries + 1} of ${ expectedRetries + 1 @@ -1215,7 +1229,7 @@ const verifySpecOutputs = ( : Array.from( { length: expectedRetries + 1 }, (_, idx) => - ` \x1B[31m ✖ should be quarantined\x1B[0m${ + ` \x1B[31m ${FAIL_SYMBOL} should be quarantined\x1B[0m${ expectedRetries > 0 ? `\x1B[33m (attempt ${idx + 1} of ${ expectedRetries + 1 diff --git a/packages/jest-plugin/package.json b/packages/jest-plugin/package.json index 02236e8..5e00edc 100644 --- a/packages/jest-plugin/package.json +++ b/packages/jest-plugin/package.json @@ -52,6 +52,7 @@ "exit": "^0.1.2", "jest-runner": "25.1.0 - 29", "jest-util": "25.1.0 - 29", + "rimraf": "^5.0.1", "rollup": "^3.21.1", "typescript": "^4.9.5" }, @@ -64,6 +65,6 @@ "scripts": { "build": "yarn clean && tsc --noEmit && rollup --config", "build:watch": "rollup --config --watch", - "clean": "rm -rf dist/" + "clean": "rimraf dist/" } } diff --git a/packages/jest-plugin/rollup.config.mjs b/packages/jest-plugin/rollup.config.mjs index 66c3880..d9474d2 100644 --- a/packages/jest-plugin/rollup.config.mjs +++ b/packages/jest-plugin/rollup.config.mjs @@ -1,5 +1,6 @@ // Copyright (c) 2023 Developer Innovations, LLC +import path from "path"; import pluginCommonJs from "@rollup/plugin-commonjs"; import pluginDts from "rollup-plugin-dts"; import pluginJson from "@rollup/plugin-json"; @@ -14,7 +15,7 @@ import pluginTypescript from "@rollup/plugin-typescript"; */ const isExternal = (id) => !id.startsWith(".") && - !id.startsWith("/") && + !path.isAbsolute(id) && !id.startsWith("src/") && !id.startsWith("@unflakable/plugins-common/") && ![ diff --git a/packages/js-api/package.json b/packages/js-api/package.json index 6ea366b..d4347b7 100644 --- a/packages/js-api/package.json +++ b/packages/js-api/package.json @@ -32,10 +32,11 @@ }, "devDependencies": { "@types/async-retry": "^1.4.5", - "@types/node-fetch": "^2.6.2" + "@types/node-fetch": "^2.6.2", + "rimraf": "^5.0.1" }, "scripts": { - "build": "rm -rf dist && tsc --noEmit && tsc -p src", + "build": "rimraf dist && tsc --noEmit && tsc -p src", "build:watch": "tsc --build --watch" } } diff --git a/packages/plugins-common/package.json b/packages/plugins-common/package.json index ac4dd1b..9315964 100644 --- a/packages/plugins-common/package.json +++ b/packages/plugins-common/package.json @@ -24,10 +24,11 @@ "@unflakable/js-api": "workspace:^", "cosmiconfig": "^7.0.1", "debug": "^4.3.3", + "rimraf": "^5.0.1", "simple-git": "^3.16.0" }, "scripts": { - "build": "rm -rf dist && tsc --noEmit && tsc -p src", + "build": "rimraf dist && tsc --noEmit && tsc -p src", "build:watch": "tsc --build --watch" } } diff --git a/packages/plugins-common/src/index.ts b/packages/plugins-common/src/index.ts index c934470..32218ac 100644 --- a/packages/plugins-common/src/index.ts +++ b/packages/plugins-common/src/index.ts @@ -1,5 +1,7 @@ // Copyright (c) 2023 Developer Innovations, LLC +import path from "path"; + export { QuarantineMode, UnflakableConfig, @@ -23,3 +25,8 @@ export { } from "./git"; export { getTestSuiteManifest } from "./manifest"; export { isTestQuarantined, normalizeTestName } from "./quarantine"; + +// On Windows, we need to convert backslashes to forward slashes before reporting results to the +// backend or checking whether tests are quarantined. +export const toPosix = (file: string): string => + file.split(path.sep).join(path.posix.sep); diff --git a/packages/plugins-common/src/quarantine.ts b/packages/plugins-common/src/quarantine.ts index 0fc27f5..70cd1b6 100644 --- a/packages/plugins-common/src/quarantine.ts +++ b/packages/plugins-common/src/quarantine.ts @@ -31,13 +31,13 @@ export const normalizeTestName = (fullTestName: string[]): string[] => export const isTestQuarantined = ( manifest: TestSuiteManifest, - testFilename: string, + posixTestFilename: string, fullTestName: string[] ): boolean => { const testName = normalizeTestName(fullTestName); return manifest.quarantined_tests.some( (quarantinedTest) => - quarantinedTest.filename === testFilename && + quarantinedTest.filename === posixTestFilename && deepEqual(quarantinedTest.name, testName) ); }; diff --git a/yarn.lock b/yarn.lock index d418eb2..ca4db0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1577,6 +1577,20 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.1.0 resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" @@ -1976,6 +1990,13 @@ __metadata: languageName: node linkType: hard +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f + languageName: node + linkType: hard + "@pkgr/utils@npm:^2.3.1": version: 2.3.1 resolution: "@pkgr/utils@npm:2.3.1" @@ -2102,6 +2123,508 @@ __metadata: languageName: node linkType: hard +"@stdlib/assert-has-node-buffer-support@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-has-node-buffer-support@npm:0.0.8" + dependencies: + "@stdlib/assert-is-buffer": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + bin: + has-node-buffer-support: bin/cli + checksum: 30fa31c3c675ce1e2b235327a5c074f7bc0a50d57eaf63d985b021073164ae1ef4be1bed842d5b56773218b0088f4fa1aa09fd488a450956a7dd088825b02864 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-has-own-property@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/assert-has-own-property@npm:0.0.7" + checksum: 4f1efbd2352214898792fea5bf83d190f9899a28d2eab52bca95de19370c112a385961390c423863be9977851f22003aee2268c1806138ad20d90bfa8d87eb95 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-has-symbol-support@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-has-symbol-support@npm:0.0.8" + dependencies: + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + bin: + has-symbol-support: bin/cli + checksum: 122d53efd4f8489a975486f1c79ec302102158f7bc92b3b13f936c8eae01189c2bf93483adb3942a310357425f9a4251959793e9a77ef310b2944af44396c8fc + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-has-tostringtag-support@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/assert-has-tostringtag-support@npm:0.0.9" + dependencies: + "@stdlib/assert-has-symbol-support": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + bin: + has-tostringtag-support: bin/cli + checksum: e2d10af88f36ba0ae9e0a205761eb6c6497b4dba037fdd1302c345c419c3ff33dcc9e3162b83a98816a079ac26415e28bdfc160ae2822941d7bec41d516be2e4 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-array@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/assert-is-array@npm:0.0.7" + dependencies: + "@stdlib/utils-native-class": ^0.0.x + checksum: 17d64b5982fa6f1feb093e8c8a631adc780035070168b92ceafb8cd5162ad3f9a81952a4938dd6515a8caee20f24aca9112e4f219eb2f03789a8030aa434ad07 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-boolean@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-boolean@npm:0.0.8" + dependencies: + "@stdlib/assert-has-tostringtag-support": ^0.0.x + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: 501a78fa4a60816fe24de8870d2bccf1cdc2f6a1d2d4ae2b4d10ba6b55f667fc4a658504dc12fe89c697aa6f81020f14c9c8cb5512b0e24d33f88c705028b7b7 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-buffer@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-buffer@npm:0.0.8" + dependencies: + "@stdlib/assert-is-object-like": ^0.0.x + checksum: 2548c1d0a4c0240bef5677fb422c419b14dbc78a6499404eecd85afc332994660284c67adbf9f2de7ae429bed0b8683491528ba75e4baa3ec374748a569c1eaa + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-function@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-function@npm:0.0.8" + dependencies: + "@stdlib/utils-type-of": ^0.0.x + checksum: 57017ac110fa14ea61efd48b8b61c35141a5f6438312a846d38b30b7e22d8048e38c267bccf91f3c0e6d47f893e5ba75f93816d1dc529595955dfb2a22ddfd6e + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-object-like@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-object-like@npm:0.0.8" + dependencies: + "@stdlib/assert-tools-array-function": ^0.0.x + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + checksum: 45b1dbf8dc43abac2890afc21d64773bc2cc932696d7372ca5366402b7351314921293b14722454ab1a7a324449dfb924e519d82df188b37f9bfc8cc54ba863b + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-object@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-object@npm:0.0.8" + dependencies: + "@stdlib/assert-is-array": ^0.0.x + checksum: e50b56c32a6693f73f7d65a29726e76b4df8ebae601fd3508b3ef512a1988ff2ce9d436607555888979c715c13600a1a0ebb205fdb0235a30ca3de5580f164ae + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-plain-object@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/assert-is-plain-object@npm:0.0.7" + dependencies: + "@stdlib/assert-has-own-property": ^0.0.x + "@stdlib/assert-is-function": ^0.0.x + "@stdlib/assert-is-object": ^0.0.x + "@stdlib/utils-get-prototype-of": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: 3ec502a4954ef959dc478d4674f1fd4b0929bb5d420be0097db6a8eb899a1e65c23bdbdfb73e1b750f058058ac0634b1fc26b4275eb8ccffa196967a3da427d7 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-regexp-string@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/assert-is-regexp-string@npm:0.0.9" + dependencies: + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + "@stdlib/process-read-stdin": ^0.0.x + "@stdlib/regexp-eol": ^0.0.x + "@stdlib/regexp-regexp": ^0.0.x + "@stdlib/streams-node-stdin": ^0.0.x + bin: + is-regexp-string: bin/cli + checksum: 10155625e04f3d79466993e080b2cf01742bf88a1adb9d383d1854b3a7464792ed8d59e810c7a35b5a8fbbfb63eb0317a540fd687cec2afdf59052b79a734911 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-regexp@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/assert-is-regexp@npm:0.0.7" + dependencies: + "@stdlib/assert-has-tostringtag-support": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: 6d881f57b1e295b0abf1f7698404cfa9aa8f0851099b5d1cb786b809742df266f17e4287af8932351a303825d475a6d6e22751a4f521b6e6504acab6d9aabd33 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-is-string@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/assert-is-string@npm:0.0.8" + dependencies: + "@stdlib/assert-has-tostringtag-support": ^0.0.x + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: c1bde204d24c2debf348202b432fa2376f0d53d8949a8a2f1cebc298e417495242267732bdb87df693ba46de3f2777895ed09d5271ba8ca83e107a7412e23c84 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/assert-tools-array-function@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/assert-tools-array-function@npm:0.0.7" + dependencies: + "@stdlib/assert-is-array": ^0.0.x + checksum: 2092c89eae72b5da2192fedecc1cddfd7ac553298425039d895cc858ee981df67f13560db60387b166354761cd74017bc5cc959e555d317c07358153bd9d79ef + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/buffer-ctor@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/buffer-ctor@npm:0.0.7" + dependencies: + "@stdlib/assert-has-node-buffer-support": ^0.0.x + checksum: 211b5eb199a6037381339bd0f7759f0dbeb912e8312d6ccb2a7c58fc77a4d311998efaecc1fce0e689e0483c213e9a24cfe3441e0130ca099f11abf5fb25cde7 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/buffer-from-string@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/buffer-from-string@npm:0.0.8" + dependencies: + "@stdlib/assert-is-function": ^0.0.x + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/buffer-ctor": ^0.0.x + "@stdlib/string-format": ^0.0.x + checksum: f436d106e816339f6ae8e438b5d36e8e522c50958663370bc3c028061aeae225026549f6de2fd50c9d34c08f1c581130dd8702b22a7002c5bdd8bd137d9ad863 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/cli-ctor@npm:^0.0.x": + version: 0.0.3 + resolution: "@stdlib/cli-ctor@npm:0.0.3" + dependencies: + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + "@stdlib/utils-noop": ^0.0.x + minimist: ^1.2.0 + checksum: c3b6e8b9d149b96a1e97df91e77fbc5245a92b504c277d993d2c08ff263ce46fc83dd211ee71e0e0641f700d7622b88265ce9a73b7628176f16626e8abe0e213 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/fs-read-file@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/fs-read-file@npm:0.0.8" + dependencies: + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + bin: + read-file: bin/cli + checksum: 10a1ead656b8a5d7f6f4975eb0d65c385f827e106452fb055caf9a471b672da33b6f282ea5a91cfb294a7d94152ab7133529ddaabdbfa63706253ba88c50bd0d + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/process-read-stdin@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/process-read-stdin@npm:0.0.7" + dependencies: + "@stdlib/assert-is-function": ^0.0.x + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/buffer-ctor": ^0.0.x + "@stdlib/buffer-from-string": ^0.0.x + "@stdlib/streams-node-stdin": ^0.0.x + "@stdlib/utils-next-tick": ^0.0.x + checksum: 5dd79b3e9939906af94025845f52f5107f9b371a3eabc131a3b456219b37f5df7f95ba04d526e37c0c8128d263f21d7371d98e77afc80fc06e926836e7150bf2 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/regexp-eol@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/regexp-eol@npm:0.0.7" + dependencies: + "@stdlib/assert-has-own-property": ^0.0.x + "@stdlib/assert-is-boolean": ^0.0.x + "@stdlib/assert-is-plain-object": ^0.0.x + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + checksum: c4cbe08a20192681395958ebcb4fdb3adaf243d229d8d681308903fb4587a990f3e8cea09ae101dd36e9d0fdff0e1206418001c99863c72a8d6e0670dd57ce65 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/regexp-extended-length-path@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/regexp-extended-length-path@npm:0.0.7" + dependencies: + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + checksum: 21a9595c94a1ce39a2b41c13346f50300ebc1a6822708336ac2433bf3dabfc312be863bb3e94e6e5aa42793df457d4aab429d6299351cddb369cac9238fbcc6f + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/regexp-function-name@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/regexp-function-name@npm:0.0.7" + dependencies: + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + checksum: 47ac8ca5b7cec2b0716de4919ac9c9c3b05af22aaa0955d458e58d330688ec3cd6742988c05703ea75373a9ffdec13e0961a8c977126dfc22ed61c0787cadbf3 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/regexp-regexp@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/regexp-regexp@npm:0.0.8" + dependencies: + "@stdlib/utils-define-nonenumerable-read-only-property": ^0.0.x + checksum: 2a771dc4485fd7c05243627bb60b8de7395d53b7dac737952d16a55773b5912adc8a5cd4191b061e3739cd725662c8101c40c9900066e2faf04e51f11b8ff5d0 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/streams-node-stdin@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/streams-node-stdin@npm:0.0.7" + checksum: 750415b3c1cbb56592e9fbd9d7ccbb3b4e7f49be3f5ba95e1156efd0382ba23bf47eb3cf4088b5f6948a76c212af6b9e81ab78c24762d513b790bd8424700240 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/string-base-format-interpolate@npm:^0.0.x": + version: 0.0.4 + resolution: "@stdlib/string-base-format-interpolate@npm:0.0.4" + checksum: 6eec4db6689fc7db21b85d70a944995721d5dcc6805f7a0c1c90070ed574e831252d252ce121abf4c76d2fdf0290ba04687cd867b23bd7f8229bd4e11b4df7fd + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/string-base-format-tokenize@npm:^0.0.x": + version: 0.0.4 + resolution: "@stdlib/string-base-format-tokenize@npm:0.0.4" + checksum: 40a18396c16c75ecfc9b517119a276f5bdcc3f45ef6107ed6d3f4549e2be3b88a5eee38149dd5010a5f79bd091add273b162cbe2a95af90a86749744a0dc32dd + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/string-format@npm:^0.0.x": + version: 0.0.3 + resolution: "@stdlib/string-format@npm:0.0.3" + dependencies: + "@stdlib/string-base-format-interpolate": ^0.0.x + "@stdlib/string-base-format-tokenize": ^0.0.x + checksum: b66be4fb1afa0d9b1ba3ee9fd00e0cf4d466e15e209f349d123e542292fa2489a1e2253dd170e87182ef7a51d103e2458df44f4ca3a697add6795525736982fe + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/string-lowercase@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/string-lowercase@npm:0.0.9" + dependencies: + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + "@stdlib/process-read-stdin": ^0.0.x + "@stdlib/streams-node-stdin": ^0.0.x + "@stdlib/string-format": ^0.0.x + bin: + lowercase: bin/cli + checksum: 278ababf4abf70eb08b58a79b9049a42ea867a51ab4391eca07afddc6ea643b7ceed8cbd5484f10c5602e1b7c89c7f98a68ed4a4f92c99b093919f10fe3a2d7d + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/string-replace@npm:^0.0.x": + version: 0.0.11 + resolution: "@stdlib/string-replace@npm:0.0.11" + dependencies: + "@stdlib/assert-is-function": ^0.0.x + "@stdlib/assert-is-regexp": ^0.0.x + "@stdlib/assert-is-regexp-string": ^0.0.x + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + "@stdlib/process-read-stdin": ^0.0.x + "@stdlib/regexp-eol": ^0.0.x + "@stdlib/streams-node-stdin": ^0.0.x + "@stdlib/string-format": ^0.0.x + "@stdlib/utils-escape-regexp-string": ^0.0.x + "@stdlib/utils-regexp-from-string": ^0.0.x + bin: + replace: bin/cli + checksum: 63c28624c18caf0557dcfb3a4837d9188b390e8a7ac31b5bdc1f8299d84812bed7f83f49128b7faefc2a4b5d67ec46997243f2c524c6e013d9ba7f9811625d67 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/types@npm:^0.0.x": + version: 0.0.14 + resolution: "@stdlib/types@npm:0.0.14" + checksum: 5680a655ddb3ad730f5c7eb2363a43e089f3e6a1b85b12546cab49f7749bb3baf293bd50fbfe55486f233f4227f1020b65eb461b754b94fb4a4bc2799647ec22 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-constructor-name@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/utils-constructor-name@npm:0.0.8" + dependencies: + "@stdlib/assert-is-buffer": ^0.0.x + "@stdlib/regexp-function-name": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: a8ad0c7db0b34d0d6015f5a01907cdfedfe9419b11cc949bd3ec28f63f29f402e6c101217ab658d8c0e815eb5c8f0672bcf9dae9a3582f4b56bef5157c2fac4c + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-convert-path@npm:^0.0.8": + version: 0.0.8 + resolution: "@stdlib/utils-convert-path@npm:0.0.8" + dependencies: + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/cli-ctor": ^0.0.x + "@stdlib/fs-read-file": ^0.0.x + "@stdlib/process-read-stdin": ^0.0.x + "@stdlib/regexp-eol": ^0.0.x + "@stdlib/regexp-extended-length-path": ^0.0.x + "@stdlib/streams-node-stdin": ^0.0.x + "@stdlib/string-lowercase": ^0.0.x + "@stdlib/string-replace": ^0.0.x + bin: + convert-path: bin/cli + checksum: d8eb6dd6b2530b05af7a58c6be2b842c5abbfb02860ba7c9a4bb035c6092d3e6781708ec00d46af97c04598b0df18ce2a81cc4068ce48907d84ba975594e52a6 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-define-nonenumerable-read-only-property@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/utils-define-nonenumerable-read-only-property@npm:0.0.7" + dependencies: + "@stdlib/types": ^0.0.x + "@stdlib/utils-define-property": ^0.0.x + checksum: 0ec1480e58c25d64144cf0c88e241604a07a9faa613e0468d79e7597adfc109a57cb08afac1868180e6fe0b02cd3517b6fc2da80d03300e63e61fc0709cf8090 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-define-property@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/utils-define-property@npm:0.0.9" + dependencies: + "@stdlib/types": ^0.0.x + checksum: f9b7b20765ce7fcae7a0b57b90133be4259344f37faea929d873f46d5e4bb56c9da74fdbea26d945da94536e94b477ebd1a41708d2dbcdfe3d69208674175438 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-escape-regexp-string@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/utils-escape-regexp-string@npm:0.0.9" + dependencies: + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/string-format": ^0.0.x + checksum: ffe0d50b217fbaaf65bfcc852fe422cf1c21dca52aadf50130d7132d38f7a108081e71a3397ea8d32cf1f15a6b9a71f9dc05e8ce39b715b53faedcb1b517d21f + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-get-prototype-of@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/utils-get-prototype-of@npm:0.0.7" + dependencies: + "@stdlib/assert-is-function": ^0.0.x + "@stdlib/utils-native-class": ^0.0.x + checksum: 850f374a4b3bb49e6812827872e4e2e0cdbebefba62ab51d5888f1434f9b4c17ea56fde861899d6ca720e98260bc1f797fcd47d52651964e728474a8e1fd085a + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-global@npm:^0.0.x": + version: 0.0.7 + resolution: "@stdlib/utils-global@npm:0.0.7" + dependencies: + "@stdlib/assert-is-boolean": ^0.0.x + checksum: 64f429ae756ad7c515696e537d76fe1ea278863c2fb6bc92dcb989b2f3a8d4f258d4c3f152df2f480fb1b5af6a960c8c8a948d6a37fbf6bae229940bcc5c076e + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-native-class@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/utils-native-class@npm:0.0.8" + dependencies: + "@stdlib/assert-has-own-property": ^0.0.x + "@stdlib/assert-has-tostringtag-support": ^0.0.x + checksum: 4867eb4107d3924dfb60fa3351412c6790440e917087be6029ca89780b4a528aacaa672f588d482f5ca905842893cc6ae3cf6d0ee44ec378d1a578ae223fea37 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-next-tick@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/utils-next-tick@npm:0.0.8" + checksum: 0ef1d398e10ec35fa2c84168d9d85b110c6900405dd851071af36c4bdc42ad3f0239b501f8bde9cdfa4ee0d8540a0453e15838a6db2d3604d92fd94d6e412ec6 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-noop@npm:^0.0.x": + version: 0.0.14 + resolution: "@stdlib/utils-noop@npm:0.0.14" + checksum: 907017cedeb7329b611dc6217a1c4c5a39070dc06e3bf5c895b11786d126eea0367d83315d48519395d5ac4af713155e0f1ccf5f2741ed03701b7a76355b975c + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-regexp-from-string@npm:^0.0.x": + version: 0.0.9 + resolution: "@stdlib/utils-regexp-from-string@npm:0.0.9" + dependencies: + "@stdlib/assert-is-string": ^0.0.x + "@stdlib/regexp-regexp": ^0.0.x + "@stdlib/string-format": ^0.0.x + checksum: 78e2fd5e007744320f8b81ae753bcb58e7b702878f04fc90cb073beed79d1a35d753835eb4a17ec5a8b187b0a7587ac652f96309cb4c6d37119e615240c015d5 + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + +"@stdlib/utils-type-of@npm:^0.0.x": + version: 0.0.8 + resolution: "@stdlib/utils-type-of@npm:0.0.8" + dependencies: + "@stdlib/utils-constructor-name": ^0.0.x + "@stdlib/utils-global": ^0.0.x + checksum: de4e2a5c797010060107ae620de4b64f198f9b28a4e4a6910a6ae310b3a28594fbe47c7ab701ad2dc5d58076b5ed06e7fcf77e89ca609980aa0367c7bfcf4dcd + conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows) + languageName: node + linkType: hard + "@tootallnate/once@npm:1": version: 1.1.2 resolution: "@tootallnate/once@npm:1.1.2" @@ -2650,6 +3173,7 @@ __metadata: "@rollup/plugin-json": ^6.0.0 "@rollup/plugin-node-resolve": ^15.0.2 "@rollup/plugin-typescript": ^11.1.1 + "@stdlib/utils-convert-path": ^0.0.8 "@types/debug": ^4.1.7 "@types/deep-equal": ^1.0.1 "@types/es6-promisify": ^6.0.1 @@ -2677,6 +3201,7 @@ __metadata: mocha: =7.0.1 ms: 2.1.1 path-browserify: ^1.0.1 + rimraf: ^5.0.1 rollup: ^3.21.1 rollup-plugin-dts: ^5.3.0 simple-git: ^3.16.0 @@ -2719,6 +3244,7 @@ __metadata: exit: ^0.1.2 jest-runner: 25.1.0 - 29 jest-util: 25.1.0 - 29 + rimraf: ^5.0.1 rollup: ^3.21.1 simple-git: ^3.16.0 typescript: ^4.9.5 @@ -2738,6 +3264,7 @@ __metadata: "@types/node-fetch": ^2.6.2 async-retry: ^1.3.3 node-fetch: ^2.6.7 + rimraf: ^5.0.1 languageName: unknown linkType: soft @@ -2748,6 +3275,7 @@ __metadata: "@unflakable/js-api": "workspace:^" cosmiconfig: ^7.0.1 debug: ^4.3.3 + rimraf: ^5.0.1 simple-git: ^3.16.0 languageName: unknown linkType: soft @@ -3063,6 +3591,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -3088,6 +3623,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + "anymatch@npm:^3.0.3, anymatch@npm:~3.1.1": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -4188,6 +4730,7 @@ __metadata: "@unflakable/plugins-common": "workspace:^" debug: ^4.3.3 expect: ^29.5.0 + rimraf: ^5.0.1 rollup: ^3.21.1 rollup-plugin-dts: ^5.3.0 simple-git: ^3.16.0 @@ -4261,7 +4804,6 @@ __metadata: resolution: "cypress-integration@workspace:packages/cypress-plugin/test/integration" dependencies: "@types/jest": ^29.5.2 - "@unflakable/cypress-plugin": "workspace:^" "@unflakable/jest-plugin": "workspace:^" "@unflakable/js-api": "workspace:^" "@unflakable/plugins-common": "workspace:^" @@ -4596,6 +5138,13 @@ __metadata: languageName: node linkType: hard +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed + languageName: node + linkType: hard + "ecc-jsbn@npm:~0.1.1": version: 0.1.2 resolution: "ecc-jsbn@npm:0.1.2" @@ -4641,6 +5190,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 8487182da74aabd810ac6d6f1994111dfc0e331b01271ae01ec1eb0ad7b5ecc2bbbbd2f053c05cb55a1ac30449527d819bbfbf0e3de1023db308cbcb47f86601 + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -5521,6 +6077,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 139d270bc82dc9e6f8bc045fe2aae4001dc2472157044fdfad376d0a3457f77857fa883c1c8b21b491c6caade9a926a4bed3d3d2e8d3c9202b151a4cbbd0bcd5 + languageName: node + linkType: hard + "forever-agent@npm:~0.6.1": version: 0.6.1 resolution: "forever-agent@npm:0.6.1" @@ -5830,6 +6396,21 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.2.5": + version: 10.3.0 + resolution: "glob@npm:10.3.0" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 + path-scurry: ^1.7.0 + bin: + glob: dist/cjs/src/bin.js + checksum: 6fa4ac0a86ffec1c5715a2e6fbdd63e1e7f1c2c8f5db08cc3256cdfcb81093678e7c80a3d100b502a1b9d141369ecf87bc24fe2bcb72acec7b14626d358a4eb0 + languageName: node + linkType: hard + "glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -6781,6 +7362,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^2.0.3": + version: 2.2.1 + resolution: "jackspeak@npm:2.2.1" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: e29291c0d0f280a063fa18fbd1e891ab8c2d7519fd34052c0ebde38538a15c603140d60c2c7f432375ff7ee4c5f1c10daa8b2ae19a97c3d4affe308c8360c1df + languageName: node + linkType: hard + "jest-changed-files@npm:^29.5.0": version: 29.5.0 resolution: "jest-changed-files@npm:29.5.0" @@ -7656,6 +8250,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^9.1.1": + version: 9.1.2 + resolution: "lru-cache@npm:9.1.2" + checksum: d3415634be3908909081fc4c56371a8d562d9081eba70543d86871b978702fffd0e9e362b83921b27a29ae2b37b90f55675aad770a54ac83bb3e4de5049d4b15 + languageName: node + linkType: hard + "magic-string@npm:^0.27.0": version: 0.27.0 resolution: "magic-string@npm:0.27.0" @@ -7838,6 +8439,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.1": + version: 9.0.2 + resolution: "minimatch@npm:9.0.2" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2eb12e2047a062fdb973fb51b9803f2455e3a00977858c038d66646d303a5a15bbcbc6ed5a2fc403bc869b1309f829ed3acd881d3246faf044ea7a494974b924 + languageName: node + linkType: hard + "minimist@npm:0.0.8": version: 0.0.8 resolution: "minimist@npm:0.0.8" @@ -7912,6 +8522,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0 || ^6.0.2": + version: 6.0.2 + resolution: "minipass@npm:6.0.2" + checksum: d140b91f4ab2e5ce5a9b6c468c0e82223504acc89114c1a120d4495188b81fedf8cade72a9f4793642b4e66672f990f1e0d902dd858485216a07cd3c8a62fac9 + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -8553,6 +9170,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.7.0": + version: 1.9.2 + resolution: "path-scurry@npm:1.9.2" + dependencies: + lru-cache: ^9.1.1 + minipass: ^5.0.0 || ^6.0.2 + checksum: 92888dfb68e285043c6d3291c8e971d5d2bc2f5082f4d7b5392896f34be47024c9d0a8b688dd7ae6d125acc424699195474927cb4f00049a9b1ec7c4256fa8e0 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -9139,6 +9766,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^5.0.1": + version: 5.0.1 + resolution: "rimraf@npm:5.0.1" + dependencies: + glob: ^10.2.5 + bin: + rimraf: dist/cjs/src/bin.js + checksum: bafce85391349a2d960847980bf9b5caa2a8887f481af630f1ea27e08288217293cec72d75e9a2ba35495c212789f66a7f3d23366ba6197026ab71c535126857 + languageName: node + linkType: hard + "rimraf@npm:~2.6.2": version: 2.6.3 resolution: "rimraf@npm:2.6.3" @@ -9408,6 +10046,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.0.2 + resolution: "signal-exit@npm:4.0.2" + checksum: 41f5928431cc6e91087bf0343db786a6313dd7c6fd7e551dbc141c95bb5fb26663444fd9df8ea47c5d7fc202f60aa7468c3162a9365cbb0615fc5e1b1328fe31 + languageName: node + linkType: hard + "simple-git@npm:^3.16.0": version: 3.17.0 resolution: "simple-git@npm:3.17.0" @@ -9616,7 +10261,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -9648,6 +10293,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + "string.prototype.trim@npm:^1.2.7": version: 1.2.7 resolution: "string.prototype.trim@npm:1.2.7" @@ -9706,6 +10362,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + "strip-ansi@npm:^4.0.0": version: 4.0.0 resolution: "strip-ansi@npm:4.0.0" @@ -9724,12 +10389,12 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + ansi-regex: ^6.0.1 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d languageName: node linkType: hard @@ -10641,6 +11306,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + "wrap-ansi@npm:^5.1.0": version: 5.1.0 resolution: "wrap-ansi@npm:5.1.0" @@ -10663,14 +11339,14 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 languageName: node linkType: hard From 59986abc3ff2f42ed4763c3505da43e9a96a9831 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Thu, 29 Jun 2023 00:24:13 -0700 Subject: [PATCH 04/12] [cypress] Fix integration tests to tolerate slow test color The Mocha reporter prints the test duration in either medium (33m) or fast (90m) color, depending on the duration. This is a source of flakiness unless the tests accept both. --- .../test/integration/src/verify-output.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/cypress-plugin/test/integration/src/verify-output.ts b/packages/cypress-plugin/test/integration/src/verify-output.ts index 8a0b6fc..6512caf 100644 --- a/packages/cypress-plugin/test/integration/src/verify-output.ts +++ b/packages/cypress-plugin/test/integration/src/verify-output.ts @@ -142,7 +142,7 @@ const verifySpecOutputs = ( // eslint-disable-next-line no-control-regex `^ {2}\x1B\\[35m {2}${PASS_SYMBOL}\x1B\\[39m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] @@ -170,7 +170,7 @@ const verifySpecOutputs = ( // eslint-disable-next-line no-control-regex `^ {2}\x1B\\[33m {2}${PASS_SYMBOL}\x1B\\[0m\x1B\\[90m mixed: flake should be quarantined\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] @@ -209,7 +209,7 @@ const verifySpecOutputs = ( // eslint-disable-next-line no-control-regex `^ {2}\x1B\\[35m {2}${PASS_SYMBOL}\x1B\\[39m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ) : expectExt.stringMatching( @@ -217,7 +217,7 @@ const verifySpecOutputs = ( // eslint-disable-next-line no-control-regex `^ {2}\x1B\\[33m {2}${PASS_SYMBOL}\x1B\\[0m\x1B\\[90m mixed: should be flaky\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] @@ -226,7 +226,7 @@ const verifySpecOutputs = ( expectExt.stringMatching( // eslint-disable-next-line no-control-regex new RegExp( - `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m mixed: should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m mixed: should pass\\x1B\\[0m\\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\\x1B\\[0m$` ) ), " \x1B[36m - mixed: should be skipped\x1B[0m", @@ -709,7 +709,7 @@ const verifySpecOutputs = ( expectedFlakeTestNameSuffix )}\x1B\\[0m\x1B\\[35m \\[flaky, quarantined]\x1B\\[39m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ) : expectExt.stringMatching( @@ -719,7 +719,7 @@ const verifySpecOutputs = ( expectedFlakeTestNameSuffix )}\x1B\\[0m\x1B\\[33m \\[flaky]\x1B\\[0m\x1B\\[33m \\(attempt 2 of ${ expectedRetries + 1 - }\\)\x1B\\[0m\x1B\\[90m \\([0-9]+.+?\\)\x1B\\[0m$` + }\\)\x1B\\[0m\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\x1B\\[0m$` ) ), ] @@ -943,7 +943,7 @@ const verifySpecOutputs = ( expectExt.stringMatching( // eslint-disable-next-line no-control-regex new RegExp( - `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should fail due to hook\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should fail due to hook\\x1B\\[0m\\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\\x1B\\[0m$` ) ), ] @@ -980,7 +980,7 @@ const verifySpecOutputs = ( expectExt.stringMatching( // eslint-disable-next-line no-control-regex new RegExp( - `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should be skipped\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should be skipped\\x1B\\[0m\\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\\x1B\\[0m$` ) ), ] @@ -1102,14 +1102,14 @@ const verifySpecOutputs = ( expectExt.stringMatching( // eslint-disable-next-line no-control-regex new RegExp( - `^\\x1B\\[32m +${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + `^\\x1B\\[32m +${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m should pass\\x1B\\[0m\\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\\x1B\\[0m$` ) ), "\x1B[0m suite name\x1B[0m", expectExt.stringMatching( // eslint-disable-next-line no-control-regex new RegExp( - `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m suite test should pass\\x1B\\[0m\\x1B\\[90m \\([0-9]+.+?\\)\\x1B\\[0m$` + `^ {2}\\x1B\\[32m {2}${PASS_SYMBOL}\\x1B\\[0m\\x1B\\[90m suite test should pass\\x1B\\[0m\\x1B\\[(?:33|90)m \\([0-9]+.+?\\)\\x1B\\[0m$` ) ), ], From 3cb5f34e3db48735e9b0fd75ef00ed418b5075ed Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Wed, 28 Jun 2023 23:06:09 -0700 Subject: [PATCH 05/12] Support Cypress 12.16 --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6c09c64..95e3ce6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -113,6 +113,7 @@ jobs: - "12.10" - "12.14" - "12.15" + - "12.16" steps: - uses: actions/checkout@v3 @@ -215,6 +216,7 @@ jobs: - "12.10" - "12.14" - "12.15" + - "12.16" steps: - uses: actions/checkout@v3 From 9bc5c477704a6d4ff4afab17035104b9aec788a7 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Thu, 29 Jun 2023 16:01:42 -0700 Subject: [PATCH 06/12] Add debug logging to Windows integration tests --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 95e3ce6..d2fe661 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -279,7 +279,7 @@ jobs: # DEBUG: unflakable:* # Enable debug logs within the Cypress plugin. - TEST_DEBUG: unflakable:* + TEST_DEBUG: "unflakable:*,cypress:server:*" # Enable terminal colors for debug() output. DEBUG_COLORS: "1" From 0f24a24b6d928965876476533a4ae9d735d23173 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Thu, 29 Jun 2023 20:11:42 -0700 Subject: [PATCH 07/12] [cypress] Use Microsoft Edge for Windows integration tests Chrome is slow on Windows, and Electron fails randomly with opaque errors. --- .../cypress-plugin/test/integration/src/parse-output.ts | 9 ++++++--- .../cypress-plugin/test/integration/src/run-test-case.ts | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/cypress-plugin/test/integration/src/parse-output.ts b/packages/cypress-plugin/test/integration/src/parse-output.ts index a2cb307..17ad736 100644 --- a/packages/cypress-plugin/test/integration/src/parse-output.ts +++ b/packages/cypress-plugin/test/integration/src/parse-output.ts @@ -207,9 +207,12 @@ const parseRunStarting = ( expect(tableEntries["Cypress"]).toMatch(/^[0-9]+\.[0-9]+\.[0-9]+$/); expect(tableEntries["Browser"]).toMatch( - // Electron is much faster on Windows than Chrome, and only a tiny bit slower on Linux, so we - // just use it for all the integration tests. - /^Electron [0-9]+ \x1B\[90m\(headless\)\x1B\[39m$/ + new RegExp( + `^${ + // Chrome is slow to launch on Windows. + process.platform === "win32" ? "Edge" : "Chrome" + } [0-9]+ \x1B\\[90m\\(headless\\)\x1B\\[39m$` + ) ); expect(tableEntries["Node Version"]).toMatch( new RegExp( diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index 4422667..bf8fa83 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -835,6 +835,9 @@ export const runTestCase = async ( "--", // e2e/component `--${params.testMode}`, + // Chrome is faster than Electron, at least on Mac. However, it's much slower on Windows. + "--browser", + process.platform === "win32" ? "edge" : "chrome", ...(params.specNameStubs !== undefined ? [ "--spec", From 288f9dcc101bac1418dd1d84599f413b6cf387ea Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Sun, 2 Jul 2023 15:23:15 -0700 Subject: [PATCH 08/12] [cypress] Disable debug colors within Cypress integration tests The `debug` library only prints timestamped output when terminal colors are disabled. We disable colors here so that we can better understand the timing of failed test runs. Note that DEBUG_COLORS is still enabled for the Jest tests that invoke the Cypress plugin integration tests, since GitHub Actions lets us show timestamps for each line of that output even if colors are enabled. However, output from individual test cases is buffered and printed all at once in the event of a test failure, so the buffered output needs to include timestamps if we want to understand the timing within a single Cypress invocation. --- packages/cypress-plugin/test/integration/src/run-test-case.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index bf8fa83..ec014bc 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -855,8 +855,6 @@ export const runTestCase = async ( const env = { ...params.envVars, DEBUG: process.env.TEST_DEBUG, - // Enable terminal colors for debug() output. - DEBUG_COLORS: "1", // Ensure Cypress prints output with TTY colors. FORCE_COLOR: "1", // NODE_OPTIONS: "--loader=testdouble", From ec88a42fada17fc5396cd2b7c9b29719ebc12f03 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Sun, 2 Jul 2023 16:17:07 -0700 Subject: [PATCH 09/12] [cypress] Bump @unflakable/cypress-plugin to 0.2.0 --- packages/cypress-plugin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cypress-plugin/package.json b/packages/cypress-plugin/package.json index d0f0bed..1dba670 100644 --- a/packages/cypress-plugin/package.json +++ b/packages/cypress-plugin/package.json @@ -8,7 +8,7 @@ "bugs": "https://github.com/unflakable/unflakable-javascript/issues", "homepage": "https://unflakable.com", "license": "MIT", - "version": "0.1.2", + "version": "0.2.0", "exports": { ".": { "types": "./dist/index.d.ts", From d4c9a284dfad984a2d9c738bb030d97990456a86 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Sun, 2 Jul 2023 23:03:26 -0700 Subject: [PATCH 10/12] [cypress] Run CI in Windows 2019 instead of 2022 This may improve performance due to https://github.com/actions/runner-images/issues/5166. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d2fe661..30afa6a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -192,7 +192,7 @@ jobs: cypress_windows_integration_tests: name: "Cypress ${{ matrix.cypress }} Windows Node ${{ matrix.node }} Integration Tests" - runs-on: windows-2022 + runs-on: windows-2019 # Cypress on Windows is slowwww... timeout-minutes: 90 needs: From 59da108021a29261297844cb3ad183eca75da096 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Mon, 3 Jul 2023 03:38:16 -0700 Subject: [PATCH 11/12] [cypress] Fix integration test timeout race When a test times out, we call treeKill() on the Cypress process tree, followed by rejecting the promise. However, killing the Cypress process triggers the `exit` event callback, which causes us to resolve the promise before we have a chance to reject it. --- .../test/integration/src/run-test-case.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index ec014bc..7d6d7cb 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -770,12 +770,12 @@ export const runTestCase = async ( multipleHookErrors, } = params; - const fetchMismatch = { error: undefined as unknown | undefined }; + const testError = { error: undefined as unknown | undefined }; const { unmatchedApiRequestEndpoint, unmatchedObjectStoreRequestEndpoint } = await addFetchMockExpectations(params, summaryTotals, (error) => { - if (fetchMismatch.error === undefined) { - fetchMismatch.error = error ?? new Error("undefined error"); + if (testError.error === undefined) { + testError.error = error ?? new Error("undefined error"); } else { console.error("Multiple failed fetch expectations", error); } @@ -963,9 +963,13 @@ export const runTestCase = async ( console.error( `Test timed out after ${TEST_TIMEOUT_MS}ms; killing Cypress process tree` ); - treeKill(cypressChild.pid, "SIGKILL", () => { - reject(new Error(`Test timed out after ${TEST_TIMEOUT_MS}ms`)); - }); + const timeoutError = new Error( + `Test timed out after ${TEST_TIMEOUT_MS}ms` + ); + if (testError.error === undefined) { + testError.error = timeoutError; + } + treeKill(cypressChild.pid, "SIGKILL", () => reject(timeoutError)); }, TEST_TIMEOUT_MS); cypressChild.on("error", (err) => { @@ -979,8 +983,8 @@ export const runTestCase = async ( } ); - if (fetchMismatch.error !== undefined) { - throw fetchMismatch.error; + if (testError.error !== undefined) { + throw testError.error; } verifyOutput(params, stdoutLines, summaryTotals, apiServer.port); From 0f8718984ee8538fd213151d6de9c32b2ac19184 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Mon, 3 Jul 2023 22:33:39 -0700 Subject: [PATCH 12/12] [cypress] Log timestamp for each line of integration test output --- .../test/integration/src/run-test-case.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/cypress-plugin/test/integration/src/run-test-case.ts b/packages/cypress-plugin/test/integration/src/run-test-case.ts index 7d6d7cb..2a66fc4 100644 --- a/packages/cypress-plugin/test/integration/src/run-test-case.ts +++ b/packages/cypress-plugin/test/integration/src/run-test-case.ts @@ -888,7 +888,7 @@ export const runTestCase = async ( const onOutput = ( name: string, - onLine: (line: string) => void, + onLine: (line: string, now: Date) => void, escapeDebugOutput: boolean ): ((data: Buffer) => void) => { const debugExt = debug.extend(name); @@ -899,12 +899,13 @@ export const runTestCase = async ( // Don't eat the last line of output. cypressChild.on("exit", () => { if (pending.s !== "") { - onLine(pending.s); + onLine(pending.s, new Date()); debugExt(escapeDebugOutput ? JSON.stringify(pending.s) : pending.s); } }); return (data: Buffer): void => { + const now = new Date(); // In case data terminates in the middle of a Unicode sequence, we need to use a stateful // TextDecoder with `stream: true`. Otherwise, invalid UTF-8 sequences at the end get // converted to 0xFFFD, which breaks the tests non-deterministically (i.e., makes them flaky). @@ -914,7 +915,7 @@ export const runTestCase = async ( // partial line that we want to defer until the next call. lines.slice(0, lines.length - 1).forEach((line, idx) => { const lineWithPending = idx === 0 ? pending.s + line : line; - onLine(lineWithPending); + onLine(lineWithPending, now); debugExt( escapeDebugOutput ? JSON.stringify(lineWithPending) : lineWithPending ); @@ -931,7 +932,9 @@ export const runTestCase = async ( "data", onOutput( "stderr", - combinedLines.push.bind(combinedLines), + (line, now) => { + combinedLines.push(`${now.toISOString()} ${line}`); + }, // Don't escape stderr output since it likely comes from debug output in the subprocess, which // is intended for human consumption and not for verifying test results. false @@ -941,9 +944,9 @@ export const runTestCase = async ( "data", onOutput( "stdout", - (line) => { + (line, now) => { stdoutLines.push(line); - combinedLines.push(line); + combinedLines.push(`${now.toISOString()} ${line}`); }, // Escape special characters in debug output so that we can more easily understand test // failures related to unexpected output. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy