Skip to content

Commit 516ba53

Browse files
authored
Setup integration tests (tailwindlabs#5466)
* setup integration tests * fix rgb color syntax * ensure integration tests always exit If for any reason the integration tests fail, then it will run forever on CI (~2hours or something). The `--forceExit` is not ideal but it will prevent long running processes. * fix incorrect test We were never properly waiting for the command to finish. * handle AbortError properly In CI, when an AbortController gets aborted an error is thrown (AbortError). If we don't catch it properly then it will "leak" and the test will fail. * improve IO functions * quit integration tests after 10seconds * only test a few integrations * test all integrations using matrix This will cancel other builds when one fails, it will also separate the output per integration which can be useful especially now that we are still figuring things out. * rename `build` to `test` * add --verbose flag to receive output in the console * when reading stdout or stderr, wait a certain about to ensure stability Debouncing for 200ms means that if another message comes in within those 200ms we delay the execution of the callback. * simplify workflow * use terminal output instead of disk events * cache node_modules for integrations * empty commit, to test cache hits
1 parent fda1c44 commit 516ba53

26 files changed

+304
-233
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Integration Tests
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
integration: [parcel, postcss-cli, rollup, tailwindcss-cli, vite, webpack-4, webpack-5]
16+
node-version: [16]
17+
18+
steps:
19+
- uses: actions/checkout@v2
20+
- name: Use Node.js ${{ matrix.node-version }}
21+
uses: actions/setup-node@v2
22+
with:
23+
node-version: ${{ matrix.node-version }}
24+
25+
- name: Use cached node_modules (tailwindcss)
26+
id: cache-tailwindcss
27+
uses: actions/cache@v2
28+
with:
29+
path: node_modules
30+
key: nodeModules-${{ hashFiles('**/package-lock.json') }}-${{ matrix.node-version }}-tailwindcss
31+
restore-keys: |
32+
nodeModules-
33+
34+
- name: Install dependencies
35+
if: steps.cache-tailwindcss.outputs.cache-hit != 'true'
36+
run: npm install
37+
env:
38+
CI: true
39+
40+
- name: Build Tailwind CSS
41+
run: npm run prepublishOnly
42+
43+
- name: Use cached node_modules (integrations)
44+
id: cache-integrations
45+
uses: actions/cache@v2
46+
with:
47+
path: ./integrations/**/node_modules
48+
key: nodeModules-${{ hashFiles('**/package-lock.json') }}-${{ matrix.node-version }}-${{ matrix.integration }}-integrations
49+
restore-keys: |
50+
nodeModules-
51+
52+
- name: Install shared dependencies
53+
if: steps.cache-integrations.outputs.cache-hit != 'true'
54+
run: npm run install:integrations
55+
env:
56+
CI: true
57+
58+
- name: Test ${{ matrix.integration }}
59+
run: npm test --prefix ./integrations/${{ matrix.integration }}

integrations/execute.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ afterEach(() => {
88
runningProcessess.splice(0).forEach((runningProcess) => runningProcess.stop())
99
})
1010

11+
function debounce(fn, ms) {
12+
let state = { timer: undefined }
13+
14+
return (...args) => {
15+
if (state.timer) clearTimeout(state.timer)
16+
state.timer = setTimeout(() => fn(...args), ms)
17+
}
18+
}
19+
1120
module.exports = function $(command, options = {}) {
1221
let abortController = new AbortController()
1322
let cwd = resolveToolRoot()
@@ -37,13 +46,13 @@ module.exports = function $(command, options = {}) {
3746
}
3847
}
3948

40-
function notifyNextStdoutActor() {
49+
let notifyNextStdoutActor = debounce(() => {
4150
return notifyNext(stdoutActors, stdoutMessages)
42-
}
51+
}, 200)
4352

44-
function notifyNextStderrActor() {
53+
let notifyNextStderrActor = debounce(() => {
4554
return notifyNext(stderrActors, stderrMessages)
46-
}
55+
}, 200)
4756

4857
let runningProcess = new Promise((resolve, reject) => {
4958
let child = spawn(command, args, {
@@ -74,6 +83,12 @@ module.exports = function $(command, options = {}) {
7483
combined += data
7584
})
7685

86+
child.on('error', (err) => {
87+
if (err.name !== 'AbortError') {
88+
throw err
89+
}
90+
})
91+
7792
child.on('close', (code, signal) => {
7893
;(signal === 'SIGTERM' ? resolve : code === 0 ? resolve : reject)({
7994
code,

integrations/io.js

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ let chokidar = require('chokidar')
66

77
let resolveToolRoot = require('./resolve-tool-root')
88

9+
function getWatcherOptions() {
10+
return {
11+
usePolling: true,
12+
interval: 200,
13+
awaitWriteFinish: {
14+
stabilityThreshold: 1500,
15+
pollInterval: 50,
16+
},
17+
}
18+
}
19+
920
module.exports = function ({
1021
/** Output directory, relative to the tool. */
1122
output = 'dist',
@@ -95,6 +106,10 @@ module.exports = function ({
95106
file = await resolveFile(file, absoluteOutputFolder)
96107
return fs.readFile(path.resolve(absoluteOutputFolder, file), 'utf8')
97108
},
109+
async readInputFile(file) {
110+
file = await resolveFile(file, absoluteInputFolder)
111+
return fs.readFile(path.resolve(absoluteInputFolder, file), 'utf8')
112+
},
98113
async appendToInputFile(file, contents) {
99114
let filePath = path.resolve(absoluteInputFolder, file)
100115
if (!fileCache[filePath]) {
@@ -122,48 +137,41 @@ module.exports = function ({
122137
async waitForOutputFileCreation(file) {
123138
if (file instanceof RegExp) {
124139
let r = file
125-
let watcher = chokidar.watch(absoluteOutputFolder)
140+
let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
126141

127142
return new Promise((resolve) => {
128143
watcher.on('add', (file) => {
129144
if (r.test(file)) {
130-
watcher.close()
131-
resolve()
145+
watcher.close().then(() => resolve())
132146
}
133147
})
134148
})
135149
} else {
136150
let filePath = path.resolve(absoluteOutputFolder, file)
137-
let watcher = chokidar.watch(filePath)
138151

139-
let watcherPromise = new Promise((resolve) => {
140-
watcher.once('add', () => {
141-
watcher.close()
142-
resolve()
152+
return new Promise((resolve) => {
153+
let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
154+
155+
watcher.on('add', (addedFile) => {
156+
if (addedFile !== filePath) return
157+
return watcher.close().finally(resolve)
143158
})
144159
})
145-
146-
if (existsSync(filePath)) {
147-
watcher.close()
148-
return Promise.resolve()
149-
}
150-
151-
return watcherPromise
152160
}
153161
},
154162
async waitForOutputFileChange(file, cb = () => {}) {
155163
file = await resolveFile(file, absoluteOutputFolder)
156-
157164
let filePath = path.resolve(absoluteOutputFolder, file)
158-
let watcher = chokidar.watch(filePath)
159165

160166
return new Promise((resolve) => {
161-
let chain = Promise.resolve()
162-
watcher.once('change', () => {
163-
watcher.close()
164-
chain.then(() => resolve())
165-
})
166-
chain.then(() => cb())
167+
let watcher = chokidar.watch(absoluteOutputFolder, getWatcherOptions())
168+
169+
watcher
170+
.on('change', (changedFile) => {
171+
if (changedFile !== filePath) return
172+
return watcher.close().finally(resolve)
173+
})
174+
.on('ready', cb)
167175
})
168176
},
169177
}

integrations/package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrations/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"version": "0.0.0",
55
"scripts": {
6-
"test": "jest --runInBand"
6+
"test": "jest --runInBand --forceExit"
77
},
88
"jest": {
99
"testTimeout": 30000,

integrations/parcel/package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrations/parcel/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
"scripts": {
66
"build": "parcel build ./src/index.html --no-cache",
77
"dev": "parcel watch ./src/index.html --no-cache",
8-
"test": "jest --runInBand"
8+
"test": "jest --runInBand --forceExit"
99
},
1010
"jest": {
11+
"testTimeout": 10000,
1112
"displayName": "parcel",
1213
"setupFilesAfterEnv": [
1314
"<rootDir>/../../jest/customMatchers.js"

integrations/parcel/tests/integration.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe.skip('watcher', () => {
8080
css`
8181
.bg-red-500 {
8282
--tw-bg-opacity: 1;
83-
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
83+
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
8484
}
8585
.font-bold {
8686
font-weight: 700;
@@ -240,7 +240,7 @@ describe.skip('watcher', () => {
240240
.btn {
241241
border-radius: 0.25rem;
242242
--tw-bg-opacity: 1;
243-
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
243+
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
244244
padding-left: 0.5rem;
245245
padding-right: 0.5rem;
246246
padding-top: 0.25rem;

integrations/postcss-cli/package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integrations/postcss-cli/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
"private": true,
44
"version": "0.0.0",
55
"scripts": {
6-
"build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css",
7-
"test": "jest --runInBand"
6+
"build": "NODE_ENV=production postcss ./src/index.css -o ./dist/main.css --verbose",
7+
"test": "jest --runInBand --forceExit"
88
},
99
"jest": {
10+
"testTimeout": 10000,
1011
"displayName": "PostCSS CLI",
1112
"setupFilesAfterEnv": [
1213
"<rootDir>/../../jest/customMatchers.js"

0 commit comments

Comments
 (0)