diff --git a/.github/workflows/actions/get-core-dependencies/action.yml b/.github/workflows/actions/get-core-dependencies/action.yml index 186ae4e2053..4c7d1ee0b80 100644 --- a/.github/workflows/actions/get-core-dependencies/action.yml +++ b/.github/workflows/actions/get-core-dependencies/action.yml @@ -6,13 +6,13 @@ runs: # this overrides previous versions of the node runtime that was set. # jobs that need a different version of the Node runtime should explicitly # set their node version after running this step - - name: Use Node Version from Volta - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node Version from Volta + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './package.json' cache: 'npm' - - name: Install Dependencies + - name: πŸ•ΈοΈ Install Dependencies run: | npm ci \ && npm run install.jest diff --git a/.github/workflows/actions/publish-npm/action.yml b/.github/workflows/actions/publish-npm/action.yml index 695bd22a8cd..6a51913752c 100644 --- a/.github/workflows/actions/publish-npm/action.yml +++ b/.github/workflows/actions/publish-npm/action.yml @@ -10,29 +10,29 @@ inputs: runs: using: 'composite' steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Set Version + - name: 🏷️ Set Version run: npm version --no-git-tag-version ${{ inputs.version }} shell: bash - - name: Prepare NPM Token + - name: πŸ“¦ Prepare NPM Token run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc shell: bash env: NPM_TOKEN: ${{ inputs.token }} - - name: Publish to NPM + - name: πŸ“€ Publish to NPM run: npm publish --tag ${{ inputs.tag }} --provenance shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c1041d7456..92daf961ce1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,25 +16,25 @@ jobs: os: ['ubuntu-22.04', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Core Build + - name: πŸ—οΈ Core Build run: npm run build -- --ci shell: bash - - name: Validate Build + - name: πŸ§ͺ Validate Build run: npm run test.dist shell: bash - - name: Validate Testing + - name: πŸ§ͺ Validate Testing run: npm run test.testing shell: bash - - name: Upload Build Artifacts + - name: πŸ“€ Upload Build Artifacts if: ${{ matrix.os == 'ubuntu-22.04' }} uses: ./.github/workflows/actions/upload-archive with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..2f48ece224e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +on: + pull_request: + push: + +jobs: + test-core: + name: Core Jest + runs-on: ubuntu-latest + steps: + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: 🟒 Use Node + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + with: + node-version: 20 + cache: 'npm' + - name: πŸ•ΈοΈ Install + run: npm ci + - name: πŸ—οΈ Build (generate testing artifacts) + run: npm run build -- --ci + - name: πŸ§ͺ Run Jest (core) + run: npm run test.jest + + test-bundler: + name: Bundler Jest + runs-on: ubuntu-latest + needs: test-core + steps: + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: 🟒 Setup Node + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + with: + node-version: 20 + cache: 'npm' + - name: πŸ•ΈοΈ Install + run: npm ci + - name: πŸ—οΈ Build core (required by bundler job) + run: npm run build -- --ci + - name: πŸ—οΈ Build bundler artifacts + run: npm run --prefix test/bundler build + - name: πŸ§ͺ Run Bundler tests + run: npm run test.bundler diff --git a/.github/workflows/create-production-pr.yml b/.github/workflows/create-production-pr.yml index 0f9f728f57f..e1bf268ea69 100644 --- a/.github/workflows/create-production-pr.yml +++ b/.github/workflows/create-production-pr.yml @@ -32,13 +32,13 @@ jobs: pull-requests: write steps: # Log the input from GitHub Actions for easy traceability - - name: Log GitHub Input + - name: πŸ”Log GitHub Input run: | echo "Version: ${{ inputs.version }}" shell: bash - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # A depth of 0 gets the entire git history, which we'll want for things like checking all git history/tags. # We need git history to generate the changelog; however, we don't know how deep to go. @@ -46,19 +46,19 @@ jobs: fetch-depth: 0 ref: ${{ inputs.base }} - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies # TODO(STENCIL-927): Backport changes to the v3 branch - - name: Run Publish Preparation Script + - name: πŸ“¦ Run Publish Preparation Script run: npm run release.ci.prepare -- --version ${{ inputs.version }} shell: bash - - name: Log Generated Changes + - name: πŸ“ Log Generated Changes run: git --no-pager diff shell: bash - - name: Generate Version String and Branch Name + - name: 🏷️ Generate Version String and Branch Name id: name_gen run: | VERSION_STR=$(jq '.version' package.json | sed s/\"//g) @@ -66,13 +66,13 @@ jobs: echo "BRANCH_NAME=release/$VERSION_STR-run-${{ github.run_number }}-${{ github.run_attempt }}" >> "$GITHUB_OUTPUT" shell: bash - - name: Print Version String and Branch Name + - name: πŸ–¨οΈ Print Version String and Branch Name run: | echo Version: ${{ steps.name_gen.outputs.VERSION_STR }} echo Branch Name: ${{ steps.name_gen.outputs.BRANCH_NAME }} shell: bash - - name: Create the Pull Request + - name: πŸ“€ Create the Pull Request uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: # create a new pull request using the specified base branch diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml index 299f384e976..37abe43e384 100644 --- a/.github/workflows/lint-and-format.yml +++ b/.github/workflows/lint-and-format.yml @@ -14,18 +14,18 @@ jobs: name: Check runs-on: 'ubuntu-22.04' steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: ESLint + - name: πŸ–ŒοΈ ESLint run: npm run lint - - name: Prettier Check + - name: πŸ–ŒοΈ Prettier Check run: npm run prettier.dry-run shell: bash - - name: Spellcheck + - name: πŸ“š Spellcheck run: npm run spellcheck diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index d3d466932cf..8873d1f8751 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -25,20 +25,20 @@ jobs: outputs: dev-version: ${{ steps.get-dev-version.outputs.DEV_VERSION }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Get Version + - name: 🏷️ Get Version id: get-dev-version run: | # A unique string to publish Stencil under @@ -56,15 +56,15 @@ jobs: shell: bash release-stencil-dev-build: - name: Publish Dev Build + name: πŸ“€ Publish Dev Build needs: [get-dev-version, build_core] runs-on: ubuntu-22.04 permissions: contents: read id-token: write steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/workflows/actions/publish-npm with: tag: dev diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 900eeee4ea6..d2199662980 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -24,20 +24,20 @@ jobs: outputs: nightly-version: ${{ steps.get-nightly-version.outputs.NIGHTLY_VERSION }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Get Version + - name: 🏷️ Get Version id: get-nightly-version run: | # A unique string to publish Stencil under @@ -58,15 +58,15 @@ jobs: shell: bash release-stencil-nightly-build: - name: Publish Nightly Build + name: πŸ“€ Publish Nightly Build needs: [get-nightly-version, build_core] runs-on: ubuntu-22.04 permissions: contents: read id-token: write steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/workflows/actions/publish-npm with: tag: nightly diff --git a/.github/workflows/release-production.yml b/.github/workflows/release-production.yml index b6488963971..bd877fc00bb 100644 --- a/.github/workflows/release-production.yml +++ b/.github/workflows/release-production.yml @@ -42,8 +42,8 @@ jobs: shell: bash if: ${{ inputs.base != 'main' && inputs.tag == 'latest' }} - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # A depth of 0 gets the entire git history, which we'll want for things like checking all git history/tags. # We need git history to generate the changelog; however, we don't know how deep to go. @@ -51,16 +51,16 @@ jobs: fetch-depth: 0 ref: ${{ inputs.base }} - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Prepare NPM Token + - name: πŸ“¦ Prepare NPM Token run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc shell: bash env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Run Publish Scripts + - name: πŸ“¦ Run Publish Scripts # pass the generated version number instead of the input, since we've already incremented it in the prerelease # step run: npm run release.ci -- --tag ${{ inputs.tag }} diff --git a/.github/workflows/test-analysis.yml b/.github/workflows/test-analysis.yml index 49b903365ef..18a1c34e7fc 100644 --- a/.github/workflows/test-analysis.yml +++ b/.github/workflows/test-analysis.yml @@ -18,28 +18,28 @@ jobs: os: ['ubuntu-latest', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Analysis Tests + - name: πŸ§ͺ Analysis Tests run: npm run test.analysis shell: bash - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-bundlers.yml b/.github/workflows/test-bundlers.yml index 3a80a6cbd30..3e38180f7ef 100644 --- a/.github/workflows/test-bundlers.yml +++ b/.github/workflows/test-bundlers.yml @@ -13,22 +13,22 @@ jobs: name: Verify Bundlers runs-on: 'ubuntu-22.04' steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Bundler Tests + - name: πŸ§ͺ Bundler Tests run: npm run test.bundlers shell: bash - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-component-starter.yml b/.github/workflows/test-component-starter.yml index 2747d952474..0f9ca288f86 100644 --- a/.github/workflows/test-component-starter.yml +++ b/.github/workflows/test-component-starter.yml @@ -19,19 +19,19 @@ jobs: os: ['ubuntu-latest', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Create Pack Directory + - name: πŸ“¦ Create Pack Directory # `mkdir` will fail if this directory already exists. # in the next steps, we'll immediately put the packed build archive in this directory. # between that and excluding `*.tgz` files in `.gitignore`, that _should_ make it safe enough for us to later @@ -39,35 +39,35 @@ jobs: run: mkdir stencil-pack-destination shell: bash - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: ./stencil-pack-destination filename: stencil-core-build.zip - - name: Copy package.json + - name: πŸ“¦ Copy package.json # need `package.json` in order to run `npm pack` run: cp package.json ./stencil-pack-destination shell: bash - - name: Copy bin + - name: πŸ“¦ Copy bin # `bin/` isn't a part of the compiled output (therefore not in the build archive). # we need this entrypoint for stencil to run. run: cp -R bin ./stencil-pack-destination shell: bash - - name: Remove node_modules + - name: πŸ“¦ Remove node_modules # clear out our local `node_modules/` so that they're not linked to in any way when `npm pack` is run run: rm -rf node_modules/ shell: bash - - name: Pack the Build Archive + - name: πŸ“¦ Pack the Build Archive run: npm pack working-directory: ./stencil-pack-destination shell: bash - - name: Move the Stencil Build Artifact + - name: πŸ“¦ Move the Stencil Build Artifact # there isn't a great way to get the output of `npm pack`, just grab the most recent from our destination # directory and hope for the best. # @@ -75,41 +75,41 @@ jobs: run: mv $(ls -t stencil-pack-destination/*.tgz | head -1) stencil-eval.tgz shell: bash - - name: Initialize Component Starter + - name: πŸ“¦ Initialize Component Starter run: npm init stencil component tmp-component-starter shell: bash - - name: Install Component Starter Dependencies + - name: πŸ•ΈοΈ Install Component Starter Dependencies run: npm install working-directory: ./tmp-component-starter shell: bash - - name: Install Stencil Eval + - name: πŸ•ΈοΈ Install Stencil Eval run: npm i ../stencil-eval.tgz working-directory: ./tmp-component-starter shell: bash - - name: Install Jest + - name: πŸ•ΈοΈ Install Jest run: npm install --dev-dependencies jest@29 jest-cli@29 @types/jest@29 - - name: Build Starter Project + - name: πŸ—οΈ Build Starter Project run: npm run build working-directory: ./tmp-component-starter shell: bash - - name: Test Starter Project + - name: πŸ§ͺ Test Starter Project run: npm run test -- --no-build # the project was just built, don't build it again working-directory: ./tmp-component-starter shell: bash - - name: Test npx stencil generate + - name: πŸ§ͺ Test npx stencil generate # `stencil generate` doesn't have a way to skip file generation, so we provide it with a component name and run # `echo` with a newline to select "all files" to generate (and use -e to interpret that backslash for a newline) run: echo -e '\n' | npm run generate -- hello-world working-directory: ./tmp-component-starter shell: bash - - name: Verify Files Exist + - name: πŸ” Verify Files Exist run: | file_list=( src/components/hello-world/hello-world.tsx @@ -128,7 +128,7 @@ jobs: working-directory: ./tmp-component-starter shell: bash - - name: Test Generated Files + - name: πŸ§ͺ Test Generated Files run: npm run test working-directory: ./tmp-component-starter shell: bash diff --git a/.github/workflows/test-copytask.yml b/.github/workflows/test-copytask.yml index 9ee47f28eb8..95f1c86d980 100644 --- a/.github/workflows/test-copytask.yml +++ b/.github/workflows/test-copytask.yml @@ -13,22 +13,22 @@ jobs: name: Verify Copy Task runs-on: 'ubuntu-22.04' steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Bundler Tests + - name: πŸ§ͺ Bundler Tests run: npm run test.copytask shell: bash - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-docs-build.yml b/.github/workflows/test-docs-build.yml index 07c329d4e93..d9b9b5a4ad2 100644 --- a/.github/workflows/test-docs-build.yml +++ b/.github/workflows/test-docs-build.yml @@ -18,28 +18,28 @@ jobs: os: ['ubuntu-latest', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Docs Build Tests + - name: πŸ§ͺ Docs Build Tests run: npm run test.docs-build shell: bash - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index de697324138..573d33b7158 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -18,31 +18,31 @@ jobs: os: ['ubuntu-latest', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: End-to-End Tests + - name: πŸ§ͺ End-to-End Tests uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 with: timeout_minutes: 10 max_attempts: 3 command: npm run test.end-to-end -- --ci - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-types.yml b/.github/workflows/test-types.yml index a8a6cfd1989..5f1fc4d3c31 100644 --- a/.github/workflows/test-types.yml +++ b/.github/workflows/test-types.yml @@ -17,25 +17,25 @@ jobs: node: ['18', '20', '22'] runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Type Tests + - name: πŸ§ͺ Type Tests run: npm run test.type-tests shell: bash diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 7c4090329b4..3d242cc4585 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -18,28 +18,28 @@ jobs: os: ['ubuntu-latest', 'windows-latest'] runs-on: ${{ matrix.os }} steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node ${{ matrix.node }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node ${{ matrix.node }} + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: ${{ matrix.node }} cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Unit Tests + - name: πŸ§ͺ Unit Tests run: npm run test.jest shell: bash - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/.github/workflows/test-wdio.yml b/.github/workflows/test-wdio.yml index 80f25aef4da..3597ac97264 100644 --- a/.github/workflows/test-wdio.yml +++ b/.github/workflows/test-wdio.yml @@ -18,31 +18,31 @@ jobs: browser: [CHROME] steps: - - name: Checkout Code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: πŸ“₯ Checkout Code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Get Core Dependencies + - name: πŸ•ΈοΈ Get Core Dependencies uses: ./.github/workflows/actions/get-core-dependencies - - name: Use Node Version from Volta - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + - name: 🟒 Use Node Version from Volta + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: # pull the version to use from the volta key in package.json node-version-file: './test/wdio/package.json' cache: 'npm' - - name: Download Build Archive + - name: πŸ“₯ Download Build Archive uses: ./.github/workflows/actions/download-archive with: name: stencil-core path: . filename: stencil-core-build.zip - - name: Run WebdriverIO Component Tests + - name: πŸ§ͺ Run WebdriverIO Component Tests run: npm run test.wdio shell: bash env: BROWSER: ${{ matrix.browser }} - - name: Check Git Context + - name: πŸ“¦ Check Git Context uses: ./.github/workflows/actions/check-git-context diff --git a/package.json b/package.json index ec68e81bbfa..0db170100d9 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,8 @@ "test.docs-build": "cd test && npm run build.docs-json && npm run build.docs-readme", "test.watch": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watch", "test.watch-all": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watchAll --coverage", + "test.bundler": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js -c test/bundler/jest.config.js --passWithNoTests", + "ci.test": "npm run test.jest && npm run test.bundler", "tsc.prod": "tsc", "ts": "tsc --noEmit --project scripts/tsconfig.json && tsx" }, diff --git a/src/sys/node/logger/terminal-logger.ts b/src/sys/node/logger/terminal-logger.ts index 1fbdce3483c..6851cbcfae5 100644 --- a/src/sys/node/logger/terminal-logger.ts +++ b/src/sys/node/logger/terminal-logger.ts @@ -95,8 +95,9 @@ export const createTerminalLogger = (loggerSys: TerminalLoggerSys): Logger => { const debug = (...msg: any[]) => { if (shouldLog(currentLogLevel, 'debug')) { - formatMemoryUsage(msg); const lines = wordWrap(msg, loggerSys.getColumns()); + // append memory usage after wrapping to preserve expected spacing semantics + lines[0] = `${lines[0]} ${getFormattedMemoryUsage()}`; debugPrefix(lines); console.log(lines.join('\n')); } @@ -115,8 +116,9 @@ export const createTerminalLogger = (loggerSys: TerminalLoggerSys): Logger => { if (debug) { if (shouldLog(currentLogLevel, 'debug')) { - formatMemoryUsage(msg); const lines = wordWrap(msg, loggerSys.getColumns()); + // append memory usage after wrapping to preserve expected spacing semantics + lines[0] = `${lines[0]} ${getFormattedMemoryUsage()}`; debugPrefix(lines); console.log(lines.join('\n')); queueWriteLog('D', [`${startMsg} ...`]); @@ -137,11 +139,12 @@ export const createTerminalLogger = (loggerSys: TerminalLoggerSys): Logger => { * * @param message an array to which the memory usage will be added */ - const formatMemoryUsage = (message: string[]) => { + const getFormattedMemoryUsage = (): string => { const mem = loggerSys.memoryUsage(); if (mem > 0) { - message.push(dim(` MEM: ${(mem / 1_000_000).toFixed(1)}MB`)); + return dim(` MEM: ${(mem / 1_000_000).toFixed(1)}MB`); } + return ''; }; const timespanFinish = ( @@ -167,9 +170,9 @@ export const createTerminalLogger = (loggerSys: TerminalLoggerSys): Logger => { if (debug) { if (shouldLog(currentLogLevel, 'debug')) { const m = [msg]; - formatMemoryUsage(m); - const lines = wordWrap(m, loggerSys.getColumns()); + // append memory usage after wrapping to preserve expected spacing semantics + lines[0] = `${lines[0]} ${getFormattedMemoryUsage()}`; debugPrefix(lines); console.log(lines.join('\n')); } diff --git a/src/sys/node/node-sys.ts b/src/sys/node/node-sys.ts index 6bf87e0fb92..2e32bd4d4e6 100644 --- a/src/sys/node/node-sys.ts +++ b/src/sys/node/node-sys.ts @@ -667,8 +667,23 @@ export function createNodeSys(c: { process?: any; logger?: Logger } = {}): Compi 'workbox-build': { minVersion: '4.3.1', recommendedVersion: '4.3.1' }, }); - prcs.on('SIGINT', runInterruptsCallbacks); - prcs.on('exit', runInterruptsCallbacks); + // In test environments avoid attaching duplicate global process listeners + const isJest = !!process.env.JEST_WORKER_ID; + if (!isJest) { + prcs.on('SIGINT', runInterruptsCallbacks); + prcs.on('exit', runInterruptsCallbacks); + } else { + const sigintHandlers = prcs.listeners('SIGINT'); + const exitHandlers = prcs.listeners('exit'); + const alreadyHasSigint = sigintHandlers.includes(runInterruptsCallbacks as any); + const alreadyHasExit = exitHandlers.includes(runInterruptsCallbacks as any); + if (!alreadyHasSigint) { + prcs.on('SIGINT', runInterruptsCallbacks); + } + if (!alreadyHasExit) { + prcs.on('exit', runInterruptsCallbacks); + } + } return sys; } diff --git a/src/testing/jest/jest-28/jest-environment.ts b/src/testing/jest/jest-28/jest-environment.ts index 4ac1530e380..e1f6ebc663a 100644 --- a/src/testing/jest/jest-28/jest-environment.ts +++ b/src/testing/jest/jest-28/jest-environment.ts @@ -1,25 +1,24 @@ -import type { Circus } from '@jest/types'; +import type { Circus, Global as JestGlobal } from '@jest/types'; import type { E2EProcessEnv, JestEnvironmentGlobal } from '@stencil/core/internal'; -import { TestEnvironment as NodeEnvironment } from 'jest-environment-node'; +import NodeEnvironment from 'jest-environment-node'; import { connectBrowser, disconnectBrowser, newBrowserPage } from '../../puppeteer/puppeteer-browser'; import { JestPuppeteerEnvironmentConstructor } from '../jest-apis'; export function createJestPuppeteerEnvironment(): JestPuppeteerEnvironmentConstructor { - const JestEnvironment = class extends NodeEnvironment { - // TODO(STENCIL-1023): Remove this @ts-expect-error - // @ts-expect-error - Stencil's Jest environment adds additional properties to the Jest global, but does not extend it - global: JestEnvironmentGlobal; + const BaseEnv = NodeEnvironment as unknown as new (...args: any[]) => any; + const JestEnvironment = class extends BaseEnv { + global: JestGlobal.Global & JestEnvironmentGlobal; browser: any = null; pages: any[] = []; testPath: string | null = null; - constructor(config: any, context: any) { + constructor(config: any, context?: any) { super(config, context); - this.testPath = context.testPath; + this.testPath = context?.testPath ?? null; } - override async setup() { + async setup() { if ((process.env as E2EProcessEnv).__STENCIL_E2E_TESTS__ === 'true') { this.global.__NEW_TEST_PAGE__ = this.newPuppeteerPage.bind(this); this.global.__CLOSE_OPEN_PAGES__ = this.closeOpenPages.bind(this); @@ -90,14 +89,14 @@ export function createJestPuppeteerEnvironment(): JestPuppeteerEnvironmentConstr this.pages.length = 0; } - override async teardown() { + async teardown() { await super.teardown(); await this.closeOpenPages(); await disconnectBrowser(this.browser); this.browser = null; } - override getVmContext() { + getVmContext() { return super.getVmContext(); } }; diff --git a/src/testing/jest/jest-28/jest-runner.ts b/src/testing/jest/jest-28/jest-runner.ts index a18ea5a30a8..02a5dc2bcb9 100644 --- a/src/testing/jest/jest-28/jest-runner.ts +++ b/src/testing/jest/jest-28/jest-runner.ts @@ -1,6 +1,6 @@ import type { AggregatedResult } from '@jest/test-result'; import type * as d from '@stencil/core/internal'; -import { default as TestRunner } from 'jest-runner'; +import TestRunner from 'jest-runner'; import type { ConfigFlags } from '../../../cli/config-flags'; import { setScreenshotEmulateData } from '../../puppeteer/puppeteer-emulate'; @@ -53,11 +53,21 @@ export async function runJest(config: d.ValidatedConfig, env: d.E2EProcessEnv) { */ export function createTestRunner(): JestTestRunnerConstructor { class StencilTestRunner extends TestRunner { - override async runTests(tests: { context: any; path: string }[], watcher: any, options: any) { + override async runTests(...args: any[]) { + const [testsArg, watcher] = args; + let onStart: any, onResult: any, onFailure: any, options: any; + const isThreeArg = args.length === 3; + if (isThreeArg) { + options = args[2]; + } else { + [, , onStart, onResult, onFailure, options] = args; + } const env = process.env as d.E2EProcessEnv; // filter out only the tests the flags said we should run - tests = tests.filter((t) => includeTestFile(t.path, env)); + const tests: { context: any; path: string }[] = (testsArg as { context: any; path: string }[]).filter( + (t: { context: any; path: string }) => includeTestFile(t.path, env), + ); if (env.__STENCIL_SCREENSHOT__ === 'true' && env.__STENCIL_EMULATE_CONFIGS__) { // we're doing e2e screenshots, so let's loop through @@ -75,12 +85,18 @@ export function createTestRunner(): JestTestRunnerConstructor { setScreenshotEmulateData(emulateConfig, env); // run the test for each emulate config - await super.runTests(tests, watcher, options); + const forwarded = isThreeArg + ? [tests, watcher, options] + : [tests, watcher, onStart, onResult, onFailure, options]; + await (super.runTests as any).apply(this, forwarded); } } else { // not doing e2e screenshot tests // so just run each test once - await super.runTests(tests, watcher, options); + const forwarded = isThreeArg + ? [tests, watcher, options] + : [tests, watcher, onStart, onResult, onFailure, options]; + await (super.runTests as any).apply(this, forwarded); } } } diff --git a/src/testing/jest/jest-29/jest-environment.ts b/src/testing/jest/jest-29/jest-environment.ts index 4ac1530e380..e1f6ebc663a 100644 --- a/src/testing/jest/jest-29/jest-environment.ts +++ b/src/testing/jest/jest-29/jest-environment.ts @@ -1,25 +1,24 @@ -import type { Circus } from '@jest/types'; +import type { Circus, Global as JestGlobal } from '@jest/types'; import type { E2EProcessEnv, JestEnvironmentGlobal } from '@stencil/core/internal'; -import { TestEnvironment as NodeEnvironment } from 'jest-environment-node'; +import NodeEnvironment from 'jest-environment-node'; import { connectBrowser, disconnectBrowser, newBrowserPage } from '../../puppeteer/puppeteer-browser'; import { JestPuppeteerEnvironmentConstructor } from '../jest-apis'; export function createJestPuppeteerEnvironment(): JestPuppeteerEnvironmentConstructor { - const JestEnvironment = class extends NodeEnvironment { - // TODO(STENCIL-1023): Remove this @ts-expect-error - // @ts-expect-error - Stencil's Jest environment adds additional properties to the Jest global, but does not extend it - global: JestEnvironmentGlobal; + const BaseEnv = NodeEnvironment as unknown as new (...args: any[]) => any; + const JestEnvironment = class extends BaseEnv { + global: JestGlobal.Global & JestEnvironmentGlobal; browser: any = null; pages: any[] = []; testPath: string | null = null; - constructor(config: any, context: any) { + constructor(config: any, context?: any) { super(config, context); - this.testPath = context.testPath; + this.testPath = context?.testPath ?? null; } - override async setup() { + async setup() { if ((process.env as E2EProcessEnv).__STENCIL_E2E_TESTS__ === 'true') { this.global.__NEW_TEST_PAGE__ = this.newPuppeteerPage.bind(this); this.global.__CLOSE_OPEN_PAGES__ = this.closeOpenPages.bind(this); @@ -90,14 +89,14 @@ export function createJestPuppeteerEnvironment(): JestPuppeteerEnvironmentConstr this.pages.length = 0; } - override async teardown() { + async teardown() { await super.teardown(); await this.closeOpenPages(); await disconnectBrowser(this.browser); this.browser = null; } - override getVmContext() { + getVmContext() { return super.getVmContext(); } }; diff --git a/src/testing/jest/jest-29/jest-runner.ts b/src/testing/jest/jest-29/jest-runner.ts index a18ea5a30a8..02a5dc2bcb9 100644 --- a/src/testing/jest/jest-29/jest-runner.ts +++ b/src/testing/jest/jest-29/jest-runner.ts @@ -1,6 +1,6 @@ import type { AggregatedResult } from '@jest/test-result'; import type * as d from '@stencil/core/internal'; -import { default as TestRunner } from 'jest-runner'; +import TestRunner from 'jest-runner'; import type { ConfigFlags } from '../../../cli/config-flags'; import { setScreenshotEmulateData } from '../../puppeteer/puppeteer-emulate'; @@ -53,11 +53,21 @@ export async function runJest(config: d.ValidatedConfig, env: d.E2EProcessEnv) { */ export function createTestRunner(): JestTestRunnerConstructor { class StencilTestRunner extends TestRunner { - override async runTests(tests: { context: any; path: string }[], watcher: any, options: any) { + override async runTests(...args: any[]) { + const [testsArg, watcher] = args; + let onStart: any, onResult: any, onFailure: any, options: any; + const isThreeArg = args.length === 3; + if (isThreeArg) { + options = args[2]; + } else { + [, , onStart, onResult, onFailure, options] = args; + } const env = process.env as d.E2EProcessEnv; // filter out only the tests the flags said we should run - tests = tests.filter((t) => includeTestFile(t.path, env)); + const tests: { context: any; path: string }[] = (testsArg as { context: any; path: string }[]).filter( + (t: { context: any; path: string }) => includeTestFile(t.path, env), + ); if (env.__STENCIL_SCREENSHOT__ === 'true' && env.__STENCIL_EMULATE_CONFIGS__) { // we're doing e2e screenshots, so let's loop through @@ -75,12 +85,18 @@ export function createTestRunner(): JestTestRunnerConstructor { setScreenshotEmulateData(emulateConfig, env); // run the test for each emulate config - await super.runTests(tests, watcher, options); + const forwarded = isThreeArg + ? [tests, watcher, options] + : [tests, watcher, onStart, onResult, onFailure, options]; + await (super.runTests as any).apply(this, forwarded); } } else { // not doing e2e screenshot tests // so just run each test once - await super.runTests(tests, watcher, options); + const forwarded = isThreeArg + ? [tests, watcher, options] + : [tests, watcher, onStart, onResult, onFailure, options]; + await (super.runTests as any).apply(this, forwarded); } } } diff --git a/test/bundler/jest-dom-utils.ts b/test/bundler/jest-dom-utils.ts new file mode 100644 index 00000000000..bb880fdee62 --- /dev/null +++ b/test/bundler/jest-dom-utils.ts @@ -0,0 +1,99 @@ +import fs from 'fs'; +import path from 'path'; +import { pathToFileURL } from 'url'; + +export type DomTestUtilities = { + setupDom: (htmlPathFromRepoRoot: string) => Promise; + tearDownDom: () => void; +}; + +export function setupDomTests(document: Document): DomTestUtilities { + let testBed = document.getElementById('test-app'); + if (!testBed) { + testBed = document.createElement('div'); + testBed.id = 'test-app'; + document.body.appendChild(testBed); + } + + async function setupDom(htmlPathFromRepoRoot: string): Promise { + if (!testBed) { + throw new Error('The Stencil/Jest test bed could not be found.'); + } + const testElement = document.createElement('div'); + testElement.className = 'test-spec'; + testBed.appendChild(testElement); + + const absPath = path.resolve(process.cwd(), htmlPathFromRepoRoot.replace(/^\//, '')); + const html = fs.readFileSync(absPath, 'utf-8'); + testElement.innerHTML = html; + + // execute module scripts referenced by the built index.html so components register + const baseDir = path.dirname(absPath); + const scripts = Array.from(testElement.querySelectorAll('script')) as HTMLScriptElement[]; + for (const s of scripts) { + const type = (s.getAttribute('type') || '').toLowerCase(); + const src = s.getAttribute('src'); + if (type === 'module' && src && src.endsWith('.js')) { + const rel = src.startsWith('/') ? src.slice(1) : src; + const jsAbs = path.resolve(baseDir, rel); + const fileUrl = pathToFileURL(jsAbs); + // dynamic import to execute the built bundle + // eslint-disable-next-line no-await-in-loop + await import(fileUrl.href); + } + } + + // wait for app readiness similar to Karma helper + await new Promise((resolve) => { + let resolved = false; + + const onAppLoad = () => { + if (!resolved) { + resolved = true; + window.removeEventListener('appload', onAppLoad); + resolve(); + } + }; + + window.addEventListener('appload', onAppLoad); + + // Fallback timeout in case the 'appload' event never fires + setTimeout(() => { + if (!resolved) { + console.warn('appload event did not fire within 5 seconds, continuing anyway'); + resolved = true; + window.removeEventListener('appload', onAppLoad); + resolve(); + } + }, 5000); + }); + + await allReady(); + return testElement; + } + + function tearDownDom(): void { + if (testBed) { + testBed.innerHTML = ''; + } + } + + async function allReady(): Promise { + const promises: Promise[] = []; + const waitForDidLoad = (elm: Element): void => { + if (elm != null && elm.nodeType === 1) { + for (let i = 0; i < elm.children.length; i++) { + const childElm = elm.children[i] as any; + if (childElm.tagName && childElm.tagName.includes('-') && typeof childElm.componentOnReady === 'function') { + promises.push(childElm.componentOnReady()); + } + waitForDidLoad(childElm); + } + } + }; + waitForDidLoad(window.document.documentElement); + await Promise.all(promises).catch((e) => console.error(e)); + } + + return { setupDom, tearDownDom }; +} diff --git a/test/bundler/jest.config.js b/test/bundler/jest.config.js new file mode 100644 index 00000000000..94af467cbbf --- /dev/null +++ b/test/bundler/jest.config.js @@ -0,0 +1,17 @@ +const path = require('path'); +const base = require('../../jest.config.js'); + +const rootDir = path.resolve(__dirname, '../..'); +const modulePathIgnorePatterns = (base.modulePathIgnorePatterns || []).filter((p) => !/\\/test\//.test(p)); + +module.exports = { + rootDir, + testEnvironment: base.testEnvironment || 'jsdom', + setupFilesAfterEnv: base.setupFilesAfterEnv || ['/testing/jest-setuptestframework.js'], + transform: base.transform, + moduleNameMapper: base.moduleNameMapper, + moduleFileExtensions: base.moduleFileExtensions, + testPathIgnorePatterns: base.testPathIgnorePatterns || [], + modulePathIgnorePatterns, + testRegex: '/test/bundler/.*\\.spec\\.ts$', +}; diff --git a/test/bundler/karma-stencil-utils.ts b/test/bundler/karma-stencil-utils.ts index bca9c3cab0c..ea254dd2e61 100644 --- a/test/bundler/karma-stencil-utils.ts +++ b/test/bundler/karma-stencil-utils.ts @@ -2,7 +2,8 @@ const path = require('path'); // we must use a relative path here instead of tsconfig#paths // see https://github.com/monounity/karma-typescript/issues/315 -import * as d from '../../internal'; +// Deprecated: replaced by jest-dom-utils.ts +export {}; /** * Utilities for creating a test bed to execute HTML rendering tests against @@ -26,193 +27,4 @@ type DomTestUtilities = { * @param document a `Document` compliant entity where tests may be rendered * @returns utilities to set up the DOM and tear it down within the test bed */ -export function setupDomTests(document: Document): DomTestUtilities { - /** - * All HTML will be rendered as a child of the test bed - get it from the current document (and create it, if it - * doesn't exist) so that it is available for all future tests. - */ - let testBed = document.getElementById('test-app'); - if (!testBed) { - testBed = document.createElement('div'); - testBed.id = 'test-app'; - document.body.appendChild(testBed); - } - - /** - * @see {@link DomTestUtilities#setupDom} - */ - function setupDom(url: string): Promise { - const testElement = document.createElement('div'); - testElement.className = 'test-spec'; - - if (!testBed) { - console.error('The Stencil/Karma test bed could not be found.'); - process.exit(1); - } - - testBed.appendChild(testElement); - - return renderTest(url, testElement); - } - - /** - * Render HTML for executing tests against. - * @param url the location on disk containing the HTML to load - * @param testElement a parent HTML element to place test code in - * @returns the fully rendered HTML to test against - */ - function renderTest(url: string, testElement: HTMLElement): Promise { - // 'base' is the directory that karma will serve all assets from - url = path.join('base', url); - - return new Promise((resolve, reject) => { - /** - * Callback to be invoked following the retrieval of the file containing the HTML to load - * @param this the `XMLHttpRequest` instance that requested the HTML - */ - const indexHtmlLoaded = function (this: XMLHttpRequest): void { - if (this.status !== 200) { - reject(`404: ${url}`); - return; - } - - testElement.innerHTML = this.responseText; - - /** - * Re-generate script tags that are embedded in the loaded HTML file. - * - * Doing so allows JS files to be loaded (via script tags), when the HTML is served, without having to configure - * Karma to load the JS explicitly. This is done by adding the host/port combination to existing `src` - * attributes. - * - * Before: - * ```html - * - * ``` - * - * After: - * ```html - * - * ``` - */ - const parseAndRebuildScriptTags = () => { - const tempScripts: NodeListOf = testElement.querySelectorAll('script'); - for (let i = 0; i < tempScripts.length; i++) { - const script: HTMLScriptElement = document.createElement('script'); - if (tempScripts[i].src) { - script.src = tempScripts[i].src; - } - if (tempScripts[i].hasAttribute('nomodule')) { - script.setAttribute('nomodule', ''); - } - if (tempScripts[i].hasAttribute('type')) { - const typeAttribute = tempScripts[i].getAttribute('type'); - if (typeof typeAttribute === 'string') { - // older DOM implementations would return an empty string to designate `null` - // here, we interpret the empty string to be a valid value - script.setAttribute('type', typeAttribute); - } - } - script.innerHTML = tempScripts[i].innerHTML; - - if (tempScripts[i].parentNode) { - // the scripts were found by querying a common parent node, which _should_ still exist - tempScripts[i].parentNode!.insertBefore(script, tempScripts[i]); - tempScripts[i].parentNode!.removeChild(tempScripts[i]); - } else { - // if for some reason the parent node no longer exists, something's manipulated it while we were parsing - // the script tags. this can lead to undesirable & hard to debug behavior, fail. - reject('the parent node for script tags no longer exists. exiting.'); - } - } - }; - - parseAndRebuildScriptTags(); - - /** - * Create a listener for Stencil's "appload" event to signal to the test framework the application and its - * children have finished loading - */ - const onAppLoad = () => { - window.removeEventListener('appload', onAppLoad); - allReady().then(() => { - resolve(testElement); - }); - }; - window.addEventListener('appload', onAppLoad); - }; - - /** - * Ensure that all `onComponentReady` functions on Stencil elements in the DOM have been called before rendering - * @returns an array of promises, one for each `onComponentReady` found on a Stencil component - */ - const allReady = (): Promise => { - const promises: Promise[] = []; - - /** - * Function that recursively traverses the DOM, looking for Stencil components. Any `componentOnReady` - * functions found on Stencil components are pushed to a buffer to be run after traversing the entire DOM. - * @param elm the current element being inspected - */ - const waitForDidLoad = (elm: Element): void => { - if (elm != null && elm.nodeType === 1) { - // the element exists and is an `ELEMENT_NODE` - for (let i = 0; i < elm.children.length; i++) { - const childElm = elm.children[i]; - if (childElm.tagName.includes('-') && isHtmlStencilElement(childElm)) { - promises.push(childElm.componentOnReady()); - } - waitForDidLoad(childElm); - } - } - }; - - // recursively walk the DOM to find all `onComponentReady` functions - waitForDidLoad(window.document.documentElement); - - return Promise.all(promises).catch((e) => console.error(e)); - }; - - try { - const testHtmlRequest = new XMLHttpRequest(); - testHtmlRequest.addEventListener('load', indexHtmlLoaded); - testHtmlRequest.addEventListener('error', (err) => { - console.error('error testHtmlRequest.addEventListener', err); - reject(err); - }); - testHtmlRequest.open('GET', url); - testHtmlRequest.send(); - } catch (e: unknown) { - console.error('catch error', e); - reject(e); - } - }); - } - - /** - * @see {@link DomTestUtilities#tearDownDom} - */ - function tearDownDom(): void { - if (testBed) { - testBed.innerHTML = ''; - } - } - - return { setupDom, tearDownDom }; -} - -/** - * Type guard to verify some entity is an instance of Stencil HTML Element - * @param elm the entity to test - * @returns `true` if the entity is a Stencil HTML Element, `false` otherwise - */ -function isHtmlStencilElement(elm: unknown): elm is d.HTMLStencilElement { - // `hasOwnProperty` does not act as a type guard/narrow `elm` in any way, so we use an assertion to verify that - // `onComponentReady` is a function - return ( - elm != null && - typeof elm === 'object' && - elm.hasOwnProperty('onComponentReady') && - typeof (elm as any).onComponentReady === 'function' - ); -} +// diff --git a/test/bundler/karma.config.ts b/test/bundler/karma.config.ts index 1053a7cdacc..5b7be1f5c4b 100644 --- a/test/bundler/karma.config.ts +++ b/test/bundler/karma.config.ts @@ -28,52 +28,5 @@ const localLaunchers = { * @param config the configuration object. this object will be updated/mutated with the settings necessary to run our * tests */ -module.exports = function (config: Config): void { - config.set({ - browsers: Object.keys(localLaunchers), - colors: true, - files: [ - // general utilities for running Stencil + Karma - 'karma-stencil-utils.ts', - - // use the application built by vite - { pattern: 'vite-bundle-test/dist/index.html', nocache: true }, - { - pattern: 'vite-bundle-test/dist/**/*.js', - // don't include these files via