Skip to content

Commit dffd7ab

Browse files
committed
Playwright tests
1 parent 204feb5 commit dffd7ab

File tree

12 files changed

+301
-24
lines changed

12 files changed

+301
-24
lines changed

.github/workflows/test.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: test
2+
on: push
3+
jobs:
4+
build:
5+
runs-on: ubuntu-latest
6+
timeout-minutes: 1
7+
strategy:
8+
matrix:
9+
node-version: [18.x]
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@v3
13+
- name: Use Node.js ${{ matrix.node-version }}
14+
uses: actions/setup-node@v3
15+
with:
16+
node-version: ${{ matrix.node-version }}
17+
- name: Cache node_modules
18+
id: cache-node-modules
19+
uses: actions/cache@v3
20+
with:
21+
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
22+
path: node_modules
23+
- name: Install dependencies
24+
if: steps.cache-node-modules.outputs.cache-hit != 'true'
25+
run: npm ci
26+
- name: Install Playwright browsers
27+
run: npx playwright install --with-deps
28+
- name: Run Playwright tests
29+
run: npm run test-playwright

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
dist
1+
dist

build.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
const esbuild = require('esbuild')
2-
const fs = require('fs')
3-
const fsp = require('fs').promises
4-
const path = require('path')
2+
const fs = require('node:fs')
3+
const fsp = require('node:fs').promises
4+
const path = require('node:path')
5+
const httpdir = require('httpdir')
56

67
const srcPath = path.join(__dirname, 'src')
78
const distPath = path.join(__dirname, 'dist')
89

910
build()
1011
if (process.argv.includes('--watch')) {
11-
const httpdir = require('/usr/local/lib/node_modules/httpdir')
1212
const server = httpdir.createServer({ basePath: distPath, httpPort: 9697 })
1313
server.onStart(({ urls }) => {
1414
console.log(urls.join('\n'))

package-lock.json

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

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"author": "Johan Satgé",
66
"private": true,
77
"scripts": {
8-
"build": "node build.js"
8+
"build": "node build.js",
9+
"test-playwright": "npm run build && playwright test"
910
},
1011
"repository": {
1112
"type": "git",
@@ -25,5 +26,9 @@
2526
"htm": "^3.1.1",
2627
"preact": "^10.23.1",
2728
"prismjs": "^1.29.0"
29+
},
30+
"devDependencies": {
31+
"@playwright/test": "^1.50.1",
32+
"httpdir": "^2.1.0"
2833
}
2934
}

playwright.config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { defineConfig } = require('@playwright/test')
2+
const httpdir = require('httpdir')
3+
4+
export default defineConfig({
5+
use: {
6+
baseURL: 'http://localhost:9698',
7+
headless: true,
8+
acceptDownloads: true,
9+
},
10+
testMatch: 'tests/*.spec.js',
11+
outputDir: 'dist/tests-results',
12+
webServer: {
13+
command: 'node_modules/.bin/httpdir dist 9698',
14+
url: 'http://localhost:9698',
15+
reuseExistingServer: true,
16+
stdout: 'pipe',
17+
stderr: 'pipe',
18+
},
19+
})

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Standalone Preact Builder ⚛️
22

3+
[![Test](https://github.com/johansatge/standalone-preact-builder/actions/workflows/test.yml/badge.svg)](https://github.com/johansatge/standalone-preact-builder/actions)
4+
35
> Build custom, self-contained & self-hosted Preact script in the browser
46
57
---

src/bundler.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ function wasmJsResolver() {
3131
// Function sends back the code, sample usage code and stats
3232
export async function buildBundle(requestedImports, format) {
3333
const { bundleSource, bundleComments, usage } = getBundleSource(requestedImports, format)
34-
console.log('BUNDLE SOURCE', bundleSource)
3534
await esbuildInitPromise
3635
const params = {
3736
stdin: {
@@ -139,7 +138,8 @@ function getAppUsageWithHtm(withSignals, withUseState,) {
139138
' const value = count.value',
140139
' return html`',
141140
' <h1>Hello ${props.name}!</h1>',
142-
' <button onclick=${() => count.value += 1}>Increment (count: ${value})</button>',
141+
' <p>Count: ${count.value}</p>',
142+
' <button onclick=${() => count.value += 1}>Increment</button>',
143143
' `',
144144
' }',
145145
'',
@@ -153,7 +153,8 @@ function getAppUsageWithHtm(withSignals, withUseState,) {
153153
' const [value, setValue] = useState(0)',
154154
' return html`',
155155
' <h1>Hello ${props.name}!</h1>',
156-
' <button onclick=${() => setValue(value + 1)}>Increment (count: ${value})</button>',
156+
' <p>Count: ${value}</p>',
157+
' <button onclick=${() => setValue(value + 1)}>Increment</button>',
157158
' `',
158159
' }',
159160
'',
@@ -162,21 +163,46 @@ function getAppUsageWithHtm(withSignals, withUseState,) {
162163
}
163164
return [
164165
'',
165-
' function App(props) {',
166-
' return html`<h1>Hello ${props.name}!</h1>`',
166+
' class App extends Component {',
167+
' constructor(props) {',
168+
' super(props)',
169+
' this.state = { count: 0 }',
170+
' }',
171+
' incrementCount = () => {',
172+
' this.setState((prevState) => ({ count: prevState.count + 1 }))',
173+
' }',
174+
' render() {',
175+
' return html`',
176+
' <h1>Hello ${this.props.name}!</h1>',
177+
' <p>Count: ${this.state.count}</p>',
178+
' <button onclick=${this.incrementCount}>Increment</button>',
179+
' `',
180+
' }',
167181
' }',
168-
'',
169-
' render(html`<${App} name="World" />`, document.querySelector(\'#root\'))',
182+
' render(h(App, { name: \'World\' }), document.querySelector(\'#root\'))',
170183
]
171-
172184
}
173185

174186
function getAppUsageWithoutHtm() {
175187
return [
176-
' function App(props) {',
177-
' return h(\'h1\', null, `Hello ${props.name}!`)',
188+
'',
189+
' class App extends Component {',
190+
' constructor(props) {',
191+
' super(props)',
192+
' this.state = { count: 0 }',
193+
' }',
194+
' incrementCount = () => {',
195+
' this.setState((prevState) => ({ count: prevState.count + 1 }))',
196+
' }',
197+
' render() {',
198+
' return h(\'div\', null, [',
199+
' h(\'h1\', null, [`Hello ${this.props.name}!`]),',
200+
' h(\'p\', null, [`Count: ${this.state.count}`]),',
201+
' h(\'button\', { onClick: this.incrementCount }, [\'Increment\']),',
202+
' ])',
203+
' }',
178204
' }',
179-
' render(App({ name: \'World\' }), document.querySelector(\'#root\'))',
205+
' render(h(App, { name: \'World\' }), document.querySelector(\'#root\'))',
180206
]
181207
}
182208

src/ui.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ body {
172172
border-radius: 4px;
173173
}
174174

175+
.check-button:disabled {
176+
opacity: 0.5;
177+
cursor: default;
178+
}
179+
175180
.columns {
176181
display: grid;
177182
grid-template-columns: repeat(4, 1fr);
@@ -278,6 +283,11 @@ input[type="radio"]:checked::after {
278283
border-radius: 50%;
279284
}
280285

286+
input[type="radio"]:disabled {
287+
opacity: 0.5;
288+
cursor: default;
289+
}
290+
281291
.loader {
282292
position: absolute;
283293
box-sizing: border-box;

src/ui.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const defaultImports = {
4141
htm: ['htm'],
4242
}
4343
const mandatoryImports = {
44-
preact: ['h', 'render']
44+
preact: ['h', 'render', 'Component']
4545
}
4646

4747
const html = htm.bind(h)
@@ -159,18 +159,25 @@ function App({ defaultImports }) {
159159
Feel free to fine-tune the imports you need to get a fully customized version of Preact though!
160160
</div>
161161
`}
162-
<button class="check-button" onClick=${onCheckAllImports}>Check all</button>
163-
<button class="check-button" onClick=${onCheckNoImports}>Check none</button>
162+
<button class="check-button" onClick=${onCheckAllImports} disabled=${isLoadingBundle}>
163+
Check all
164+
</button>
165+
<button class="check-button" onClick=${onCheckNoImports} disabled=${isLoadingBundle} data-testid="check-none">
166+
Check none
167+
</button>
164168
<div class="columns">
165169
${Object.keys(window.preactEcosystem.imports).map((pkg) => html`
166170
<div class="column" key=${pkg}>
167171
<h3 class="column-title">
168172
${pkg}
169-
<span class="version">${window.preactEcosystem.versions[pkg]}</span>
173+
<span class="version" data-testid=${`${pkg}-version`}>
174+
${window.preactEcosystem.versions[pkg]}
175+
</span>
170176
</h3>
171177
<${ImportsList}
172178
pkg="${pkg}" imports=${window.preactEcosystem.imports[pkg]}
173179
selectedImports=${selectedImports} onImportChange=${onImportChange}
180+
isLoadingBundle=${isLoadingBundle}
174181
/>
175182
</div>
176183
`)}
@@ -187,6 +194,7 @@ function App({ defaultImports }) {
187194
type="radio" name="format" value="esm"
188195
onChange=${onFormatChange}
189196
checked=${format === 'esm'}
197+
disabled=${isLoadingBundle}
190198
/>
191199
ESM
192200
</label>
@@ -195,6 +203,7 @@ function App({ defaultImports }) {
195203
type="radio" name="format" value="iife"
196204
onChange=${onFormatChange}
197205
checked=${format === 'iife'}
206+
disabled=${isLoadingBundle}
198207
/>
199208
IIFE
200209
</label>
@@ -209,7 +218,9 @@ function App({ defaultImports }) {
209218
<button class="action ${hasCopied ? 'copied' : ''}" onClick=${onCopyToClipboard} disabled=${!bundle.filename}>
210219
Copy to clipboard
211220
</button>
212-
<button class="action" onClick=${onDownload} disabled=${!bundle.filename}>Download file</button>
221+
<button class="action" onClick=${onDownload} disabled=${!bundle.filename} data-testid="download-bundle">
222+
Download file
223+
</button>
213224
<span class="size">
214225
Size: ${bundle.sizeKb || 0}Kb (${bundle.sizeGzippedKb || 0}Kb gzipped)
215226
</span>
@@ -221,6 +232,7 @@ function App({ defaultImports }) {
221232
<code
222233
class="code"
223234
dangerouslySetInnerHTML=${{__html: highlightedBundleUsage}}
235+
data-testid=${!isLoadingBundle ? 'html-example' : null }
224236
></code>
225237
</pre>
226238
</section>
@@ -230,13 +242,14 @@ function App({ defaultImports }) {
230242
`
231243
}
232244

233-
function ImportsList({ pkg, imports, selectedImports, onImportChange }) {
245+
function ImportsList({ pkg, imports, selectedImports, onImportChange, isLoadingBundle }) {
234246
return imports.map((imp) => html`
235247
<label class="input-label" key=${pkg + imp}>
236248
<input
237249
type="checkbox" autocomplete="off"
238250
data-pkg=${pkg} data-imp=${imp}
239-
disabled=${mandatoryImports[pkg] && mandatoryImports[pkg].includes(imp)}
251+
data-testid=${`check-${pkg}-${imp}`}
252+
disabled=${isLoadingBundle || (mandatoryImports[pkg] && mandatoryImports[pkg].includes(imp))}
240253
checked=${typeof selectedImports[pkg] === 'object' && selectedImports[pkg].includes(imp)}
241254
value="1"
242255
onChange=${onImportChange}

0 commit comments

Comments
 (0)