Skip to content

Commit febd34c

Browse files
committed
more ergonomic e2e test environment
1 parent 0d55b43 commit febd34c

6 files changed

Lines changed: 86 additions & 102 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
node_modules
22
dist
3+
e2e/dist
34
sqljs-documentstore-*.tgz
45
**/*.generated.ts
56
.temp

e2e/bundle.cjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const esbuild = require('esbuild');
2+
const path = require('path');
3+
4+
esbuild.buildSync({
5+
entryPoints: [path.join(__dirname, '../src/index.ts')],
6+
bundle: true,
7+
format: 'iife',
8+
globalName: 'sqljsLib',
9+
outfile: path.join(__dirname, 'dist/bundle.js'),
10+
target: 'es2022',
11+
external: ['sql.js'],
12+
tsconfig: path.join(__dirname, '../tsconfig.app.json'),
13+
});

e2e/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
<!DOCTYPE html>
2-
<html><head><title>e2e test</title></head><body></body></html>
2+
<html><head><title>e2e test</title></head><body>
3+
<script src="/dist/bundle.js"></script>
4+
</body></html>

e2e/persistence.e2e.ts

Lines changed: 54 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,104 @@
11
import { test, expect } from '@playwright/test';
2-
import * as fs from 'fs';
3-
import * as path from 'path';
4-
import { fileURLToPath } from 'url';
5-
6-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
7-
8-
function getWorkerCode(): string {
9-
const generated = fs.readFileSync(path.join(__dirname, '../src/persistenceWorker.generated.ts'), 'utf8');
10-
const match = generated.match(/export const WORKER_CODE = (.*);$/ms);
11-
if (!match) throw new Error('Could not extract WORKER_CODE');
12-
return JSON.parse(match[1]);
13-
}
14-
15-
function createWorkerHelper(workerCode: string) {
16-
return `
17-
window.__worker = (() => {
18-
const blob = new Blob([${JSON.stringify(workerCode)}], { type: 'application/javascript' });
19-
const worker = new Worker(URL.createObjectURL(blob));
20-
let messageId = 0;
21-
const pending = new Map();
22-
23-
worker.onmessage = (e) => {
24-
const { id, success, error, ...rest } = e.data;
25-
const p = pending.get(id);
26-
if (!p) return;
27-
pending.delete(id);
28-
if (success) p.resolve(rest);
29-
else p.reject(new Error(error || 'Unknown worker error'));
30-
};
31-
32-
return {
33-
post(msg, transfer = []) {
34-
return new Promise((resolve, reject) => {
35-
const id = messageId++;
36-
pending.set(id, { resolve, reject });
37-
worker.postMessage({ ...msg, id }, transfer);
38-
});
39-
}
40-
};
41-
})();
42-
`;
43-
}
442

453
test.describe('sqljsPersistence', () => {
46-
let workerCode: string;
47-
48-
test.beforeAll(() => {
49-
workerCode = getWorkerCode();
50-
});
51-
524
test.beforeEach(async ({ page }) => {
535
await page.goto('/');
54-
await page.evaluate(createWorkerHelper(workerCode));
556
});
567

578
test('save and load round-trips data correctly', async ({ page }) => {
589
const result = await page.evaluate(async () => {
59-
const w = (window as any).__worker;
10+
const { sqljsPersistence } = (window as any).sqljsLib;
6011
const dbName = 'test-roundtrip-' + Date.now();
6112
const passPhrase = 'test-passphrase';
6213

63-
// Load creates a new db and returns a key
64-
const loadResult = await w.post({ type: 'load', dbName, passPhrase });
65-
if (!loadResult.isNew) throw new Error('Expected isNew to be true');
14+
const mockSqlJs = {
15+
Database: class {
16+
data: Uint8Array;
17+
constructor(data?: Uint8Array) { this.data = data ?? new Uint8Array(0); }
18+
export() { return this.data; }
19+
}
20+
};
21+
22+
const { key, salt } = await sqljsPersistence.load(dbName, passPhrase, mockSqlJs);
6623

67-
// Save some data
6824
const testData = new Uint8Array([1, 2, 3, 4, 5, 42, 99, 200]);
69-
await w.post(
70-
{ type: 'save', dbName, key: loadResult.key, salt: loadResult.salt, rawData: testData },
71-
[testData.buffer]
72-
);
73-
74-
// Load it back
75-
const loaded = await w.post({ type: 'load', dbName, passPhrase });
76-
return {
77-
isNew: loaded.isNew,
78-
data: Array.from(new Uint8Array(loaded.rawData)),
79-
};
25+
await sqljsPersistence.save(dbName, key, salt, { export: () => testData });
26+
27+
const { database: loaded } = await sqljsPersistence.load(dbName, passPhrase, mockSqlJs);
28+
return Array.from(loaded.data);
8029
});
8130

82-
expect(result.isNew).toBe(false);
83-
expect(result.data).toEqual([1, 2, 3, 4, 5, 42, 99, 200]);
31+
expect(result).toEqual([1, 2, 3, 4, 5, 42, 99, 200]);
8432
});
8533

86-
test('load returns isNew for non-existent database', async ({ page }) => {
34+
test('load returns a new empty database for non-existent db', async ({ page }) => {
8735
const result = await page.evaluate(async () => {
88-
const w = (window as any).__worker;
89-
const dbName = 'test-new-' + Date.now();
90-
const loadResult = await w.post({ type: 'load', dbName, passPhrase: 'pw' });
91-
return { isNew: loadResult.isNew };
36+
const { sqljsPersistence } = (window as any).sqljsLib;
37+
const mockSqlJs = {
38+
Database: class {
39+
data: Uint8Array;
40+
constructor(data?: Uint8Array) { this.data = data ?? new Uint8Array(0); }
41+
export() { return this.data; }
42+
}
43+
};
44+
45+
const { database } = await sqljsPersistence.load('test-new-' + Date.now(), 'pw', mockSqlJs);
46+
return database.data.length;
9247
});
9348

94-
expect(result.isNew).toBe(true);
49+
expect(result).toBe(0);
9550
});
9651

9752
test('save overwrites previous data', async ({ page }) => {
9853
const result = await page.evaluate(async () => {
99-
const w = (window as any).__worker;
54+
const { sqljsPersistence } = (window as any).sqljsLib;
10055
const dbName = 'test-overwrite-' + Date.now();
10156
const passPhrase = 'pw';
57+
const mockSqlJs = {
58+
Database: class {
59+
data: Uint8Array;
60+
constructor(data?: Uint8Array) { this.data = data ?? new Uint8Array(0); }
61+
export() { return this.data; }
62+
}
63+
};
10264

103-
const { key, salt } = await w.post({ type: 'load', dbName, passPhrase });
104-
105-
// Save first version
106-
const data1 = new Uint8Array([10, 20, 30]);
107-
await w.post({ type: 'save', dbName, key, salt, rawData: data1 });
65+
const { key, salt } = await sqljsPersistence.load(dbName, passPhrase, mockSqlJs);
10866

109-
// Save second version
110-
const data2 = new Uint8Array([77, 88]);
111-
await w.post({ type: 'save', dbName, key, salt, rawData: data2 });
67+
await sqljsPersistence.save(dbName, key, salt, { export: () => new Uint8Array([10, 20, 30]) });
68+
await sqljsPersistence.save(dbName, key, salt, { export: () => new Uint8Array([77, 88]) });
11269

113-
// Load should return second version
114-
const loaded = await w.post({ type: 'load', dbName, passPhrase });
115-
return Array.from(new Uint8Array(loaded.rawData));
70+
const { database: loaded } = await sqljsPersistence.load(dbName, passPhrase, mockSqlJs);
71+
return Array.from(loaded.data);
11672
});
11773

11874
expect(result).toEqual([77, 88]);
11975
});
12076

12177
test('dbExists returns false for non-existent database', async ({ page }) => {
12278
const result = await page.evaluate(async () => {
123-
const w = (window as any).__worker;
124-
const dbName = 'test-noexist-' + Date.now();
125-
const res = await w.post({ type: 'exists', dbName });
126-
return res.exists;
79+
const { sqljsPersistence } = (window as any).sqljsLib;
80+
return sqljsPersistence.dbExists('test-noexist-' + Date.now());
12781
});
12882

12983
expect(result).toBe(false);
13084
});
13185

13286
test('dbExists returns true after save', async ({ page }) => {
13387
const result = await page.evaluate(async () => {
134-
const w = (window as any).__worker;
88+
const { sqljsPersistence } = (window as any).sqljsLib;
13589
const dbName = 'test-exists-' + Date.now();
136-
const passPhrase = 'pw';
90+
const mockSqlJs = {
91+
Database: class {
92+
data: Uint8Array;
93+
constructor(data?: Uint8Array) { this.data = data ?? new Uint8Array(0); }
94+
export() { return this.data; }
95+
}
96+
};
13797

138-
const { key, salt } = await w.post({ type: 'load', dbName, passPhrase });
139-
const data = new Uint8Array([1, 2, 3]);
140-
await w.post({ type: 'save', dbName, key, salt, rawData: data }, [data.buffer]);
98+
const { key, salt } = await sqljsPersistence.load(dbName, 'pw', mockSqlJs);
99+
await sqljsPersistence.save(dbName, key, salt, { export: () => new Uint8Array([1, 2, 3]) });
141100

142-
const res = await w.post({ type: 'exists', dbName });
143-
return res.exists;
101+
return sqljsPersistence.dbExists(dbName);
144102
});
145103

146104
expect(result).toBe(true);

e2e/server.cjs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@ const http = require('http');
22
const fs = require('fs');
33
const path = require('path');
44

5+
const MIME = {
6+
'.html': 'text/html',
7+
'.js': 'application/javascript',
8+
};
9+
510
const server = http.createServer((req, res) => {
6-
const filePath = path.join(__dirname, 'index.html');
7-
const html = fs.readFileSync(filePath, 'utf8');
8-
res.writeHead(200, { 'Content-Type': 'text/html' });
9-
res.end(html);
11+
const filePath = path.join(__dirname, req.url === '/' ? 'index.html' : req.url);
12+
if (!fs.existsSync(filePath)) {
13+
res.writeHead(404);
14+
res.end();
15+
return;
16+
}
17+
const ext = path.extname(filePath);
18+
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
19+
res.end(fs.readFileSync(filePath));
1020
});
1121

1222
server.listen(3333, () => {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"build:worker": "tsc --project tsconfig.worker.json && node scripts/buildWorker.cjs",
1515
"build": "pnpm run build:worker && tsc --project tsconfig.app.json",
1616
"test": "vitest",
17-
"e2e": "pnpm run build:worker && playwright test"
17+
"e2e": "pnpm run build && node e2e/bundle.cjs && playwright test"
1818
},
1919
"keywords": [
2020
"SQLite",

0 commit comments

Comments
 (0)