Skip to content

Commit a0ccac4

Browse files
authored
Merge branch 'master' into master
2 parents f8c8c15 + 7147130 commit a0ccac4

12 files changed

Lines changed: 290 additions & 139 deletions

File tree

.github/actions/load/action.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ runs:
1111
uses: actions/setup-node@v6
1212
with:
1313
node-version: '20'
14-
14+
- uses: actions/github-script@v8
15+
id: platform
16+
with:
17+
result-encoding: string
18+
script: |
19+
const os = require('os');
20+
return os.platform();
1521
- name: 'Clear working directory'
1622
shell: bash
1723
run: |
18-
rm -r ./*
24+
rm -r "$(pwd)"/*
1925
2026
- name: Download artifact
2127
uses: actions/download-artifact@v5
@@ -26,5 +32,7 @@ runs:
2632
- name: 'Untar working directory'
2733
shell: bash
2834
run: |
29-
tar -xf '${{ runner.temp }}/${{ inputs.name }}.tar' .
30-
35+
wdir="$(pwd)"
36+
parent="$(dirname "$wdir")"
37+
target="$(basename "$wdir")"
38+
tar ${{ steps.platform.outputs.result == 'win32' && '--force-local' || '' }} -C "$parent" -xf '${{ runner.temp }}/${{ inputs.name }}.tar' "$target"

.github/actions/save/action.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
11
name: Save working directory
22
description: Save working directory, preserving permissions
33
inputs:
4+
prefix:
5+
description: Prefix to use for autogenerated names
6+
required: false
47
name:
58
description: a name to reference with actions/load
9+
required: false
10+
outputs:
11+
name:
12+
description: a name to reference with actions/load
13+
value: ${{ fromJSON(steps.platform.outputs.result).name }}
614

715
runs:
816
using: 'composite'
917
steps:
18+
- uses: actions/github-script@v8
19+
id: platform
20+
with:
21+
script: |
22+
const os = require('os');
23+
const crypto = require("crypto");
24+
const id = crypto.randomBytes(16).toString("hex");
25+
return {
26+
name: ${{ inputs.name && format('"{0}"', inputs.name) || format('"{0}" + id', inputs.prefix || '') }},
27+
platform: os.platform(),
28+
}
1029
- name: Tar working directory
1130
shell: bash
1231
run: |
13-
tar -cf "${{ runner.temp }}/${{ inputs.name }}.tar" .
32+
wdir="$(pwd)"
33+
parent="$(dirname "$wdir")"
34+
target="$(basename "$wdir")"
35+
tar ${{ fromJSON(steps.platform.outputs.result).platform == 'win32' && '--force-local' || '' }} -C "$parent" -cf "${{ runner.temp }}/${{ fromJSON(steps.platform.outputs.result).name }}.tar" "$target"
1436
- name: Upload artifact
1537
uses: actions/upload-artifact@v4
1638
with:
17-
path: '${{ runner.temp }}/${{ inputs.name }}.tar'
18-
name: ${{ inputs.name }}
39+
path: '${{ runner.temp }}/${{ fromJSON(steps.platform.outputs.result).name }}.tar'
40+
name: ${{ fromJSON(steps.platform.outputs.result).name }}
1941
overwrite: true
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Run unit tests on all browsers
2+
on:
3+
workflow_call:
4+
inputs:
5+
chunks:
6+
description: Number of chunks to split tests into
7+
required: false
8+
type: number
9+
default: 1
10+
build-cmd:
11+
description: Build command, run once
12+
required: false
13+
type: string
14+
test-cmd:
15+
description: Test command, run once per chunk
16+
required: true
17+
type: string
18+
timeout:
19+
description: Timeout on test run
20+
required: false
21+
type: number
22+
default: 10
23+
outputs:
24+
coverage:
25+
description: Artifact name for coverage results
26+
value: ${{ jobs.browser-tests.outputs.coverage }}
27+
secrets:
28+
BROWSERSTACK_USER_NAME:
29+
description: "Browserstack user name"
30+
BROWSERSTACK_ACCESS_KEY:
31+
description: "Browserstack access key"
32+
jobs:
33+
build:
34+
uses: ./.github/workflows/build.yml
35+
with:
36+
build-cmd: ${{ inputs.build-cmd }}
37+
38+
setup:
39+
needs: build
40+
name: "Setup environment"
41+
runs-on: ubuntu-latest
42+
outputs:
43+
browsers: ${{ toJSON(fromJSON(steps.define.outputs.result).browsers) }}
44+
bstack-key: ${{ steps.bstack-save.outputs.name }}
45+
steps:
46+
- name: Checkout
47+
uses: actions/checkout@v5
48+
- name: Restore working directory
49+
uses: ./.github/actions/load
50+
with:
51+
name: ${{ needs.build.outputs.built-key }}
52+
- name: "Define testing strategy"
53+
uses: actions/github-script@v8
54+
id: define
55+
with:
56+
script: |
57+
const fs = require('node:fs/promises');
58+
const browsers = require('./.github/workflows/browser_testing.json');
59+
const excludeFromBstack = Object.values(browsers).map(browser => browser.bsName);
60+
const bstackBrowsers = Object.fromEntries(
61+
// exlude "latest" version of browsers that we can test on GH actions
62+
Object.entries(require('./browsers.json'))
63+
.filter(([name, def]) => !excludeFromBstack.includes(def.browser) || def.browser_version !== 'latest')
64+
)
65+
const updatedBrowsersJson = JSON.stringify(bstackBrowsers, null, 2);
66+
console.log("Using browsers.json:", updatedBrowsersJson);
67+
await fs.writeFile('./browsers.json', updatedBrowsersJson);
68+
return {
69+
hasBSBrowsers: Object.keys(bstackBrowsers).length > 0,
70+
browsers: Object.entries(browsers).map(([name, def]) => Object.assign({name}, def))
71+
}
72+
- name: "Save working directory"
73+
id: bstack-save
74+
if: ${{ fromJSON(steps.define.outputs.result).hasBSBrowsers }}
75+
uses: ./.github/actions/save
76+
with:
77+
prefix: browserstack-
78+
79+
test-build-logic:
80+
needs: build
81+
name: "Test build logic"
82+
uses:
83+
./.github/workflows/run-tests.yml
84+
with:
85+
built-key: ${{ needs.build.outputs.built-key }}
86+
test-cmd: gulp test-build-logic
87+
88+
browser-tests:
89+
needs: [setup, build]
90+
name: "Browser: ${{ matrix.browser.name }}"
91+
strategy:
92+
fail-fast: false
93+
matrix:
94+
browser: ${{ fromJSON(needs.setup.outputs.browsers) }}
95+
uses:
96+
./.github/workflows/run-tests.yml
97+
with:
98+
built-key: ${{ needs.build.outputs.built-key }}
99+
test-cmd: ${{ inputs.test-cmd }} --browsers ${{ matrix.browser.name }} ${{ matrix.browser.coverage && '--coverage' || '--no-coverage' }}
100+
chunks: ${{ inputs.chunks }}
101+
runs-on: ${{ matrix.browser.runsOn || 'ubuntu-latest' }}
102+
103+
browserstack-tests:
104+
needs: setup
105+
if: ${{ needs.setup.outputs.bstack-key }}
106+
name: "Browserstack tests"
107+
uses:
108+
./.github/workflows/run-tests.yml
109+
with:
110+
built-key: ${{ needs.setup.outputs.bstack-key }}
111+
test-cmd: ${{ inputs.test-cmd }} --browserstack --no-coverage
112+
chunks: ${{ inputs.chunks }}
113+
browserstack: true
114+
secrets:
115+
BROWSERSTACK_USER_NAME: ${{ secrets.BROWSERSTACK_USER_NAME }}
116+
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"ChromeHeadless": {
3+
"bsName": "chrome",
4+
"coverage": true
5+
},
6+
"EdgeHeadless": {
7+
"bsName": "edge",
8+
"runsOn": "windows-latest"
9+
},
10+
"SafariNative": {
11+
"bsName": "safari",
12+
"runsOn": "macos-latest"
13+
},
14+
"FirefoxHeadless": {
15+
"bsName": "firefox"
16+
}
17+
}

.github/workflows/build.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Run unit tests
2+
on:
3+
workflow_call:
4+
inputs:
5+
source-key:
6+
description: Artifact name for source directory
7+
type: string
8+
required: false
9+
default: source
10+
build-cmd:
11+
description: Build command
12+
required: false
13+
type: string
14+
outputs:
15+
built-key:
16+
description: Artifact name for built directory
17+
value: ${{ jobs.build.outputs.built-key }}
18+
19+
jobs:
20+
build:
21+
name: Build
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 5
24+
outputs:
25+
built-key: ${{ inputs.build-cmd && steps.save.outputs.name || inputs.source-key }}
26+
steps:
27+
- name: Checkout
28+
if: ${{ inputs.build-cmd }}
29+
uses: actions/checkout@v5
30+
- name: Restore source
31+
if: ${{ inputs.build-cmd }}
32+
uses: ./.github/actions/load
33+
with:
34+
name: ${{ inputs.source-key }}
35+
- name: Build
36+
if: ${{ inputs.build-cmd }}
37+
run: ${{ inputs.build-cmd }}
38+
- name: 'Save working directory'
39+
id: save
40+
if: ${{ inputs.build-cmd }}
41+
uses: ./.github/actions/save
42+
with:
43+
prefix: 'build-'

.github/workflows/run-tests.yml

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ on:
1111
description: Build command, run once
1212
required: false
1313
type: string
14+
built-key:
15+
description: Artifact name for built source
16+
required: false
17+
type: string
1418
test-cmd:
1519
description: Test command, run once per chunk
1620
required: true
@@ -19,11 +23,17 @@ on:
1923
description: If true, set up browserstack environment and adjust concurrency
2024
required: false
2125
type: boolean
26+
default: false
2227
timeout:
2328
description: Timeout on test run
2429
required: false
2530
type: number
2631
default: 10
32+
runs-on:
33+
description: Runner image
34+
required: false
35+
default: ubuntu-latest
36+
type: string
2737
outputs:
2838
coverage:
2939
description: Artifact name for coverage results
@@ -35,47 +45,30 @@ on:
3545
description: "Browserstack access key"
3646

3747
jobs:
38-
build:
39-
name: Build
48+
checkout:
49+
name: "Set up environment"
4050
runs-on: ubuntu-latest
41-
timeout-minutes: 5
4251
outputs:
4352
chunks: ${{ steps.chunks.outputs.chunks }}
44-
wdir: ${{ inputs.build-cmd && format('build-{0}', inputs.build-cmd) || 'source' }}
4553
steps:
46-
- name: Checkout
47-
if: ${{ inputs.build-cmd }}
48-
uses: actions/checkout@v5
49-
- name: Restore source
50-
if: ${{ inputs.build-cmd }}
51-
uses: ./.github/actions/load
52-
with:
53-
name: source
54-
55-
- name: Build
56-
if: ${{ inputs.build-cmd }}
57-
run: ${{ inputs.build-cmd }}
58-
59-
- name: 'Save working directory'
60-
if: ${{ inputs.build-cmd }}
61-
uses: ./.github/actions/save
62-
with:
63-
name: build-${{ inputs.build-cmd }}
64-
6554
- name: Define chunks
6655
id: chunks
6756
run: |
6857
echo 'chunks=[ '$(seq --separator=, 1 1 ${{ inputs.chunks }})' ]' >> "$GITHUB_OUTPUT"
69-
70-
58+
59+
build:
60+
uses: ./.github/workflows/build.yml
61+
with:
62+
build-cmd: ${{ !inputs.built-key && inputs.build-cmd || '' }}
63+
source-key: ${{ inputs.built-key || 'source' }}
7164

7265
run-tests:
73-
needs: build
66+
needs: [checkout, build]
7467
strategy:
7568
fail-fast: false
7669
max-parallel: ${{ inputs.browserstack && 1 || inputs.chunks }}
7770
matrix:
78-
chunk-no: ${{ fromJSON(needs.build.outputs.chunks) }}
71+
chunk-no: ${{ fromJSON(needs.checkout.outputs.chunks) }}
7972

8073
name: "Test chunk ${{ matrix.chunk-no }}"
8174
env:
@@ -93,15 +86,15 @@ jobs:
9386
group: ${{ inputs.browserstack && 'browser' || github.run_id }}${{ inputs.browserstack && 'stac' || inputs.test-cmd }}${{ inputs.browserstack && 'k' || matrix.chunk-no }}-${{ github.run_id }}
9487
cancel-in-progress: false
9588

96-
runs-on: ubuntu-latest
89+
runs-on: ${{ inputs.runs-on }}
9790
steps:
9891
- name: Checkout
9992
uses: actions/checkout@v5
10093

10194
- name: Restore source
10295
uses: ./.github/actions/load
10396
with:
104-
name: ${{ needs.build.outputs.wdir }}
97+
name: ${{ needs.build.outputs.built-key }}
10598

10699
- name: 'BrowserStack Env Setup'
107100
if: ${{ inputs.browserstack }}
@@ -137,6 +130,7 @@ jobs:
137130

138131
- name: 'Check for coverage'
139132
id: 'coverage'
133+
shell: bash
140134
run: |
141135
if [ -d "./build/coverage" ]; then
142136
echo 'coverage=true' >> "$GITHUB_OUTPUT";
@@ -164,7 +158,7 @@ jobs:
164158
- name: Restore source
165159
uses: ./.github/actions/load
166160
with:
167-
name: ${{ needs.build.outputs.wdir }}
161+
name: ${{ needs.build.outputs.built-key }}
168162

169163
- name: Download coverage results
170164
uses: actions/download-artifact@v5

.github/workflows/test.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,11 @@ jobs:
9090
test:
9191
name: "Unit tests (all features enabled + coverage)"
9292
needs: checkout
93-
uses: ./.github/workflows/run-tests.yml
93+
uses: ./.github/workflows/browser-tests.yml
9494
with:
9595
chunks: 8
9696
build-cmd: npx gulp precompile
97-
test-cmd: npx gulp test-only-nobuild --browserstack
98-
browserstack: true
97+
test-cmd: npx gulp test-only-nobuild
9998
secrets:
10099
BROWSERSTACK_USER_NAME: ${{ secrets.BROWSERSTACK_USER_NAME }}
101100
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}

gulpfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ gulp.task('build-bundle-verbose', gulp.series(precompile(), makeWebpackPkg(makeV
521521
// public tasks (dependencies are needed for each task since they can be ran on their own)
522522
gulp.task('update-browserslist', execaTask('npx update-browserslist-db@latest'));
523523
gulp.task('test-build-logic', execaTask('npx mocha ./test/build-logic'))
524-
gulp.task('test-only-nobuild', gulp.series('test-build-logic', testTaskMaker({coverage: true})))
524+
gulp.task('test-only-nobuild', gulp.series(testTaskMaker({coverage: argv.coverage ?? true})))
525525
gulp.task('test-only', gulp.series('test-build-logic', 'precompile', test));
526526

527527
gulp.task('test-all-features-disabled-nobuild', testTaskMaker({disableFeatures: helpers.getTestDisableFeatures(), oneBrowser: 'chrome', watch: false}));

0 commit comments

Comments
 (0)