Skip to content

Commit 63a67cb

Browse files
authored
improve integration tests (tailwindlabs#4572)
* remove unused file using syntax.js instead * add a way to remove files that are generated from the tests Essentially setting the restore cache contents to `null` as a special value. * combine the stdout and stderr output * add jest-diff instead of relying on it existing *somewhere* This also came with a small API change. * add `--runInBand` to the test script * add dedicated cli tests for the new Tailwind CLI
1 parent f1e3168 commit 63a67cb

File tree

9 files changed

+338
-23
lines changed

9 files changed

+338
-23
lines changed

integrations/execute.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,29 @@ module.exports = function $(command, options = {}) {
5858

5959
let stdout = ''
6060
let stderr = ''
61+
let combined = ''
6162

6263
child.stdout.on('data', (data) => {
6364
stdoutMessages.push(data.toString())
6465
notifyNextStdoutActor()
6566
stdout += data
67+
combined += data
6668
})
6769

6870
child.stderr.on('data', (data) => {
6971
stderrMessages.push(data.toString())
7072
notifyNextStderrActor()
7173
stderr += data
74+
combined += data
7275
})
7376

7477
child.on('close', (code, signal) => {
75-
;(signal === 'SIGTERM' ? resolve : code === 0 ? resolve : reject)({ code, stdout, stderr })
78+
;(signal === 'SIGTERM' ? resolve : code === 0 ? resolve : reject)({
79+
code,
80+
stdout,
81+
stderr,
82+
combined,
83+
})
7684
})
7785
})
7886

integrations/html.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

integrations/io.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ module.exports = function ({
3030
// Restore all written files
3131
afterEach(async () => {
3232
await Promise.all(
33-
Object.entries(fileCache).map(([file, content]) => fs.writeFile(file, content, 'utf8'))
33+
Object.entries(fileCache).map(async ([file, content]) => {
34+
try {
35+
if (content === null) {
36+
return await fs.unlink(file)
37+
} else {
38+
return await fs.writeFile(file, content, 'utf8')
39+
}
40+
} catch {}
41+
})
3442
)
3543
})
3644

@@ -68,6 +76,21 @@ module.exports = function ({
6876
}
6977

7078
return {
79+
cleanupFile(file) {
80+
let filePath = path.resolve(toolRoot, file)
81+
fileCache[filePath] = null
82+
},
83+
async fileExists(file) {
84+
let filePath = path.resolve(toolRoot, file)
85+
return existsSync(filePath)
86+
},
87+
async removeFile(file) {
88+
let filePath = path.resolve(toolRoot, file)
89+
if (!fileCache[filePath]) {
90+
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
91+
}
92+
await fs.unlink(filePath)
93+
},
7194
async readOutputFile(file) {
7295
file = await resolveFile(file, absoluteOutputFolder)
7396
return fs.readFile(path.resolve(absoluteOutputFolder, file), 'utf8')
@@ -83,7 +106,15 @@ module.exports = function ({
83106
async writeInputFile(file, contents) {
84107
let filePath = path.resolve(absoluteInputFolder, file)
85108
if (!fileCache[filePath]) {
86-
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
109+
try {
110+
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
111+
} catch (err) {
112+
if (err.code === 'ENOENT') {
113+
fileCache[filePath] = null // Sentinel value to `delete` the file afterwards. This also means that we are writing to a `new` file inside the test.
114+
} else {
115+
throw err
116+
}
117+
}
87118
}
88119

89120
return fs.writeFile(path.resolve(absoluteInputFolder, file), contents, 'utf8')

integrations/tailwindcss-cli/package-lock.json

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

integrations/tailwindcss-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"version": "0.0.0",
55
"scripts": {
66
"build": "NODE_ENV=production node ../../lib/cli.js -i ./src/index.css -o ./dist/main.css",
7-
"test": "jest"
7+
"test": "jest --runInBand"
88
},
99
"jest": {
1010
"displayName": "Tailwind CSS CLI",
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
let path = require('path')
2+
let $ = require('../../execute')
3+
let { css, html } = require('../../syntax')
4+
let resolveToolRoot = require('../../resolve-tool-root')
5+
6+
let { readOutputFile, writeInputFile, cleanupFile, fileExists, removeFile } = require('../../io')({
7+
output: 'dist',
8+
input: 'src',
9+
})
10+
11+
let EXECUTABLE = 'node ../../lib/cli.js'
12+
13+
describe('Build command', () => {
14+
test('--output', async () => {
15+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
16+
17+
await $(`${EXECUTABLE} --output ./dist/main.css`)
18+
19+
let contents = await readOutputFile('main.css')
20+
21+
// `-i` is omitted, therefore the default `@tailwind base; @tailwind
22+
// components; @tailwind utilities` is used. However `preflight` is
23+
// disabled. I still want to verify that the `base` got included.
24+
expect(contents).toIncludeCss(
25+
css`
26+
*,
27+
::before,
28+
::after {
29+
--tw-border-opacity: 1;
30+
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
31+
}
32+
`
33+
)
34+
35+
// Verify `utilities` output is correct
36+
expect(contents).toIncludeCss(
37+
css`
38+
.font-bold {
39+
font-weight: 700;
40+
}
41+
`
42+
)
43+
})
44+
45+
test('--input, --output', async () => {
46+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
47+
48+
await $(`${EXECUTABLE} --input ./src/index.css --output ./dist/main.css`)
49+
50+
expect(await readOutputFile('main.css')).toIncludeCss(
51+
css`
52+
.font-bold {
53+
font-weight: 700;
54+
}
55+
`
56+
)
57+
})
58+
59+
test('--minify', async () => {
60+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
61+
62+
await $(`${EXECUTABLE} --output ./dist/main.css --minify`)
63+
let withMinify = await readOutputFile('main.css')
64+
65+
// Verify that we got the expected output. Note: `.toIncludeCss` formats
66+
// `actual` & `expected`
67+
expect(withMinify).toIncludeCss(
68+
css`
69+
.font-bold {
70+
font-weight: 700;
71+
}
72+
`
73+
)
74+
75+
await $(`${EXECUTABLE} --output ./dist/main.css`)
76+
let withoutMinify = await readOutputFile('main.css')
77+
78+
// Let's verify that the actual minified output is smaller than the not
79+
// minified version.
80+
expect(withoutMinify.length).toBeGreaterThan(withMinify.length)
81+
})
82+
83+
test('--no-autoprefixer', async () => {
84+
await writeInputFile('index.html', html`<div class="select-none"></div>`)
85+
86+
await $(`${EXECUTABLE} --output ./dist/main.css`)
87+
let withAutoprefixer = await readOutputFile('main.css')
88+
89+
expect(withAutoprefixer).toIncludeCss(css`
90+
.select-none {
91+
-webkit-user-select: none;
92+
user-select: none;
93+
}
94+
`)
95+
96+
await $(`${EXECUTABLE} --output ./dist/main.css --no-autoprefixer`)
97+
let withoutAutoprefixer = await readOutputFile('main.css')
98+
99+
expect(withoutAutoprefixer).toIncludeCss(css`
100+
.select-none {
101+
user-select: none;
102+
}
103+
`)
104+
})
105+
106+
test('--config (non-existing config file)', async () => {
107+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
108+
109+
let { stderr } = await $(
110+
`${EXECUTABLE} --output ./dist/main.css --config ./non-existing.config.js`
111+
).catch((err) => err)
112+
113+
let toolRoot = resolveToolRoot()
114+
expect(stderr).toEqual(
115+
`Specified config file ${path.resolve(toolRoot, 'non-existing.config.js')} does not exist.\n`
116+
)
117+
})
118+
119+
test('--config (existing config file)', async () => {
120+
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
121+
122+
let customConfig = `module.exports = ${JSON.stringify(
123+
{
124+
purge: ['./src/index.html'],
125+
mode: 'jit',
126+
darkMode: false, // or 'media' or 'class'
127+
theme: {
128+
extend: {
129+
fontWeight: {
130+
bold: 'BOLD',
131+
},
132+
},
133+
},
134+
variants: {
135+
extend: {},
136+
},
137+
corePlugins: {
138+
preflight: false,
139+
},
140+
plugins: [],
141+
},
142+
143+
null,
144+
2
145+
)}`
146+
147+
await writeInputFile('../custom.config.js', customConfig)
148+
149+
await $(`${EXECUTABLE} --output ./dist/main.css --config ./custom.config.js`)
150+
151+
expect(await readOutputFile('main.css')).toIncludeCss(
152+
css`
153+
.font-bold {
154+
font-weight: BOLD;
155+
}
156+
`
157+
)
158+
})
159+
160+
test('--help', async () => {
161+
let { combined } = await $(`${EXECUTABLE} --help`)
162+
163+
expect(combined).toMatchInlineSnapshot(`
164+
"
165+
tailwindcss v2.1.2
166+
167+
Usage:
168+
tailwindcss build [options]
169+
170+
Options:
171+
-i, --input Input file
172+
-o, --output Output file
173+
-w, --watch Watch for changes and rebuild as needed
174+
--jit Build using JIT mode
175+
--files Template files to scan for class names
176+
--postcss Load custom PostCSS configuration
177+
-m, --minify Minify the output
178+
-c, --config Path to a custom config file
179+
--no-autoprefixer Disable autoprefixer
180+
-h, --help Display usage information
181+
182+
"
183+
`)
184+
})
185+
})
186+
187+
describe('Init command', () => {
188+
test('--full', async () => {
189+
cleanupFile('full.config.js')
190+
191+
let { combined } = await $(`${EXECUTABLE} init full.config.js --full`)
192+
193+
expect(combined).toMatchInlineSnapshot(`
194+
"
195+
Created Tailwind CSS config file: full.config.js
196+
"
197+
`)
198+
199+
// Not a clean way to test this. We could require the file and verify that
200+
// multiple keys in `theme` exists. However it loads `tailwindcss/colors`
201+
// which doesn't exists in this context.
202+
expect((await readOutputFile('../full.config.js')).split('\n').length).toBeGreaterThan(50)
203+
})
204+
205+
test('--jit', async () => {
206+
cleanupFile('with-jit.config.js')
207+
208+
let { combined } = await $(`${EXECUTABLE} init with-jit.config.js --jit`)
209+
210+
expect(combined).toMatchInlineSnapshot(`
211+
"
212+
Created Tailwind CSS config file: with-jit.config.js
213+
"
214+
`)
215+
216+
expect(await readOutputFile('../with-jit.config.js')).toContain("mode: 'jit'")
217+
})
218+
219+
test('--full, --jit', async () => {
220+
cleanupFile('full-with-jit.config.js')
221+
222+
let { combined } = await $(`${EXECUTABLE} init full-with-jit.config.js --jit --full`)
223+
224+
expect(combined).toMatchInlineSnapshot(`
225+
"
226+
Created Tailwind CSS config file: full-with-jit.config.js
227+
"
228+
`)
229+
230+
expect(await readOutputFile('../full-with-jit.config.js')).toContain("mode: 'jit'")
231+
})
232+
233+
test('--postcss', async () => {
234+
expect(await fileExists('postcss.config.js')).toBe(true)
235+
await removeFile('postcss.config.js')
236+
expect(await fileExists('postcss.config.js')).toBe(false)
237+
238+
let { combined } = await $(`${EXECUTABLE} init --postcss`)
239+
240+
expect(await fileExists('postcss.config.js')).toBe(true)
241+
242+
expect(combined).toMatchInlineSnapshot(`
243+
"
244+
tailwind.config.js already exists.
245+
Created PostCSS config file: postcss.config.js
246+
"
247+
`)
248+
})
249+
250+
test('--help', async () => {
251+
let { combined } = await $(`${EXECUTABLE} init --help`)
252+
253+
expect(combined).toMatchInlineSnapshot(`
254+
"
255+
tailwindcss v2.1.2
256+
257+
Usage:
258+
tailwindcss init [options]
259+
260+
Options:
261+
--jit Initialize for JIT mode
262+
-f, --full Initialize a full \`tailwind.config.js\` file
263+
-p, --postcss Initialize a \`postcss.config.js\` file
264+
-h, --help Display usage information
265+
266+
"
267+
`)
268+
})
269+
})

jest/customMatchers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const prettier = require('prettier')
2-
const diff = require('jest-diff').default
2+
const { diff } = require('jest-diff')
33

44
function format(input) {
55
return prettier.format(input, {

0 commit comments

Comments
 (0)