diff --git a/package.json b/package.json index 86f486db..d2e4ef6c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "bin": "./build/src/index.js", "main": "index.js", "scripts": { - "build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts", + "clean": "rm -rf build", + "build": "npm run clean && tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts", "typecheck": "tsc --noEmit", "format": "eslint --cache --fix . && prettier --write --cache .", "check-format": "eslint --cache . && prettier --check --cache .;", @@ -19,6 +20,7 @@ "test:only": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"", "test:only:no-build": "node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"", "test:update-snapshots": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"", + "test:coverage": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --experimental-test-coverage --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"", "prepare": "node --experimental-strip-types scripts/prepare.ts", "sync-server-json-version": "node --experimental-strip-types scripts/sync-server-json-version.ts && npm run format" }, diff --git a/tests/Mutex.test.ts b/tests/Mutex.test.ts new file mode 100644 index 00000000..aecf6074 --- /dev/null +++ b/tests/Mutex.test.ts @@ -0,0 +1,77 @@ +import {describe, it} from 'node:test'; +import {Mutex} from '../src/Mutex.js'; +import assert from 'node:assert'; + +describe('Mutex', () => { + it('should acquire and release the lock', async () => { + const mutex = new Mutex(); + const guard = await mutex.acquire(); + guard.dispose(); + }); + + it('should ensure only one user can acquire the lock at a time', async () => { + const mutex = new Mutex(); + await mutex.acquire(); + + let acquired = false; + mutex.acquire().then(() => { + acquired = true; + }); + + // Give the promise a chance to resolve if it's not waiting for the lock. + await new Promise(resolve => setTimeout(resolve, 0)); + + assert.strictEqual(acquired, false, 'Mutex should not have been acquired'); + }); + + it('should allow acquiring the lock again after it has been released', async () => { + const mutex = new Mutex(); + const guard1 = await mutex.acquire(); + guard1.dispose(); + + const guard2 = await mutex.acquire(); + guard2.dispose(); + }); + + it('should handle multiple concurrent requests in FIFO order', async () => { + const mutex = new Mutex(); + const order: number[] = []; + + const p1 = mutex.acquire().then(guard => { + order.push(1); + guard.dispose(); + }); + + const p2 = mutex.acquire().then(guard => { + order.push(2); + guard.dispose(); + }); + + const p3 = mutex.acquire().then(guard => { + order.push(3); + guard.dispose(); + }); + + await Promise.all([p1, p2, p3]); + + assert.deepStrictEqual(order, [1, 2, 3], 'The mutex should have been acquired in FIFO order'); + }); + + it('should work with async/await', async () => { + const mutex = new Mutex(); + const guard = await mutex.acquire(); + + let acquired = false; + mutex.acquire().then(() => { + acquired = true; + }); + + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(acquired, false, 'Mutex should not have been acquired'); + + guard.dispose(); + + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(acquired, true, 'Mutex should have been acquired'); + }); +}); \ No newline at end of file diff --git a/tests/tools/screenshot.test.ts b/tests/tools/screenshot.test.ts index d369f2ca..00b3a816 100644 --- a/tests/tools/screenshot.test.ts +++ b/tests/tools/screenshot.test.ts @@ -77,8 +77,12 @@ describe('screenshot', () => { it('with full page resulting in a large screenshot', async () => { await withBrowser(async (response, context) => { const page = context.getSelectedPage(); + // This is a large page that will result in a screenshot that is >2MB. + // The original size of 7,000 was too large for Puppeteer to handle, + // causing a protocol error. This size is large enough to trigger the + // file-saving logic without causing an error. await page.setContent( - `
test
`.repeat(7_000), + `
test
`.repeat(4_000), ); await screenshot.handler( {params: {format: 'png', fullPage: true}},