diff --git a/.gitignore b/.gitignore index 5e95152..a5c1263 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ .reports *.log dist -ponyfill.bundle.js -polyfill.bundle.js +*.bundle.js # OSX # diff --git a/Makefile b/Makefile index ee80e5a..65231c6 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,6 @@ .PHONY: all all: test lint typecheck -.PHONY: browser -browser: - @./bin/server --exec "npx open-cli http://127.0.0.1:8000/test/fetch-api/browser/" - .PHONY: clean clean: @rm -Rf node_modules dist @@ -21,6 +17,9 @@ release: release-alpha: npx standard-version --prerelease alpha +.PHONY: server +server: + @./bin/server --silent --exec "echo Fetch api test suites: http://127.0.0.1:8000/test/fetch-api/" ## # Builds @@ -79,6 +78,7 @@ test: | test-browser test-node test-browser: |\ test-fetch-browser-native \ test-fetch-browser-whatwg \ + test-fetch-browser-service-worker \ test-module-web-cjs \ test-module-web-esm \ test-module-react-native @@ -106,6 +106,12 @@ test-fetch-browser-whatwg: | dist test/fetch-api/api.spec.js @echo "=> make $@" @./test/fetch-api/whatwg/run.sh +.PHONY: test-fetch-browser-service-worker +test-fetch-browser-service-worker: dist test/fetch-api/api.spec.js + @echo "" + @echo "=> make $@" + @./test/fetch-api/service-worker/run.sh + .PHONY: test-fetch-node-native test-fetch-node-native: | dist test/fetch-api/api.spec.js @echo "" diff --git a/bin/server b/bin/server index 4157e4e..86fd356 100755 --- a/bin/server +++ b/bin/server @@ -57,7 +57,9 @@ function processFileUrl (args) { } const server = app.listen(port, ip, () => { - console.log(`Test server listening at ${url}`) + if (!argv.silent) { + console.log(`Test server listening at ${url}`) + } if (argv.exec) { const args = processFileUrl(argv.exec.split(' ')) diff --git a/examples/cloud-worker/package.json b/examples/cloud-worker/package.json index 49790ad..fb70ac6 100644 --- a/examples/cloud-worker/package.json +++ b/examples/cloud-worker/package.json @@ -1,5 +1,5 @@ { - "name": "cloud-worker", + "name": "cloud-worker-fix", "version": "0.0.0", "devDependencies": { "wrangler": "2.19.0" diff --git a/examples/cloud-worker/wrangler.toml b/examples/cloud-worker/wrangler.toml index 557fd42..b895f3c 100644 --- a/examples/cloud-worker/wrangler.toml +++ b/examples/cloud-worker/wrangler.toml @@ -1,3 +1,3 @@ -name = "cloud-worker" +name = "cloud-worker-fix" main = "src/index.js" compatibility_date = "2023-05-06" diff --git a/package.json b/package.json index 5a57f34..74487d0 100644 --- a/package.json +++ b/package.json @@ -24,13 +24,15 @@ }, "standard": { "env": [ + "browser", "mocha", - "browser" + "serviceworker" ], "globals": [ "expect", "assert", - "chai" + "chai", + "Mocha" ], "ignore": [ "/dist/", @@ -87,7 +89,6 @@ "mocha-headless-chrome": "4.0.0", "nock": "13.3.0", "nyc": "15.1.0", - "open-cli": "7.2.0", "rimraf": "5.0.0", "rollup": "3.20.7", "rollup-plugin-copy": "3.4.0", diff --git a/rollup.config.js b/rollup.config.js index 759d274..dfd00e5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -25,7 +25,7 @@ export default [ name: 'irrelevant', strict: false, banner: outdent(` - // Save the global object in a variable + // Save global object in a variable var __global__ = (typeof globalThis !== 'undefined' && globalThis) || (typeof self !== 'undefined' && self) || @@ -53,8 +53,7 @@ export default [ delete __globalThis__.fetch.polyfill; // Choose between native implementation (__global__) or custom implementation (__globalThis__) - // var ctx = __global__.fetch ? __global__ : __globalThis__; - var ctx = __globalThis__ // this line disable service worker support temporarily + var ctx = __global__.fetch ? __global__ : __globalThis__; exports = ctx.fetch // To enable: import fetch from 'cross-fetch' exports.default = ctx.fetch // For TypeScript consumers without esModuleInterop. diff --git a/test/fetch-api/api.spec.js b/test/fetch-api/api.spec.js index a002cc8..0f4a7e2 100644 --- a/test/fetch-api/api.spec.js +++ b/test/fetch-api/api.spec.js @@ -425,15 +425,21 @@ function addFetchSuite() { it('should default to status 200 OK', () => { const res = new Response(); expect(res.status).to.equal(200); - // expect(res.statusText).to.equal('OK') expect(res.ok).to.equal(true); }); it('should default to status 200 OK when an explicit undefined status code is passed', () => { const res = new Response('', { status: undefined }); expect(res.status).to.equal(200); - // expect(res.statusText).to.equal('OK') expect(res.ok).to.equal(true); }); + /** + * Implementation conflict! + */ + it.skip('should have statusText as empty', () => { + const res = new Response(); + expect(res.statusText).to.equal(''); // Pass on browser native, node native, node-fetch 3+, whatwg-fetch 3.1+ + expect(res.statusText).to.equal('OK'); // Pass on node-fetch 2.6.9, whatwg-fetch 3.0 (thus, react native) + }); it('should create Headers object from raw headers', () => { const response = new Response('{"foo":"bar"}', { headers: { 'content-type': 'application/json' } @@ -530,18 +536,19 @@ function addFetchSuite() { const headers = new Headers({ Custom: undefined }); expect(headers.get('Custom')).to.equal('undefined'); }); - // Breaking change: This tests fails on Node 18+ + /** + * Implementation conflict! + */ it.skip('should throw TypeError on invalid character in field name', () => { - /* eslint-disable no-new */ - expect(function () { new Headers({ '': 'application/json' }); }).to.throw(); - expect(function () { new Headers({ 'Accept:': 'application/json' }); }).to.throw(); expect(function () { const headers = new Headers(); headers.set({ field: 'value' }, 'application/json'); }).to.throw(); + // The expects below don't pass on Node 18+ but pass in the others implementations + expect(function () { new Headers({ '': 'application/json' }); }).to.throw(); + expect(function () { new Headers({ 'Accept:': 'application/json' }); }).to.throw(); }); it('should not init an invalid header', () => { - /* eslint-disable no-new */ expect(function () { new Headers({ Héy: 'ok' }); }).to.throw(); }); it('should not set an invalid header', () => { diff --git a/test/fetch-api/api.spec.ts b/test/fetch-api/api.spec.ts index 033d265..b04e05b 100644 --- a/test/fetch-api/api.spec.ts +++ b/test/fetch-api/api.spec.ts @@ -486,17 +486,24 @@ function addFetchSuite () { it('should default to status 200 OK', () => { const res = new Response() expect(res.status).to.equal(200) - // expect(res.statusText).to.equal('OK') expect(res.ok).to.equal(true) }) it('should default to status 200 OK when an explicit undefined status code is passed', () => { const res = new Response('', { status: undefined }) expect(res.status).to.equal(200) - // expect(res.statusText).to.equal('OK') expect(res.ok).to.equal(true) }) + /** + * Implementation conflict! + */ + it.skip('should have statusText as empty', () => { + const res = new Response() + expect(res.statusText).to.equal('') // Pass on browser native, node native, node-fetch 3+, whatwg-fetch 3.1+ + expect(res.statusText).to.equal('OK') // Pass on node-fetch 2.6.9, whatwg-fetch 3.0 (thus, react native) + }) + it('should create Headers object from raw headers', () => { const response = new Response('{"foo":"bar"}', { headers: { 'content-type': 'application/json' } @@ -614,19 +621,21 @@ function addFetchSuite () { expect(headers.get('Custom')).to.equal('undefined') }) - // Breaking change: This tests fails on Node 18+ + /** + * Implementation conflict! + */ it.skip('should throw TypeError on invalid character in field name', () => { - /* eslint-disable no-new */ - expect(function (): void { new Headers({ '': 'application/json' }) }).to.throw() - expect(function (): void { new Headers({ 'Accept:': 'application/json' }) }).to.throw() expect(function (): void { const headers = new Headers() headers.set({ field: 'value' } as any, 'application/json') }).to.throw() + + // The expects below don't pass on Node 18+ but pass in the others implementations + expect(function (): void { new Headers({ '': 'application/json' }) }).to.throw() + expect(function (): void { new Headers({ 'Accept:': 'application/json' }) }).to.throw() }) it('should not init an invalid header', () => { - /* eslint-disable no-new */ expect(function (): void { new Headers({ Héy: 'ok' }) }).to.throw() }) diff --git a/test/fetch-api/browser/index.html b/test/fetch-api/browser/index.html index 3c7ec73..c4e45f2 100644 --- a/test/fetch-api/browser/index.html +++ b/test/fetch-api/browser/index.html @@ -19,7 +19,7 @@ - + + + + diff --git a/test/fetch-api/service-worker/run.sh b/test/fetch-api/service-worker/run.sh new file mode 100755 index 0000000..feb1b55 --- /dev/null +++ b/test/fetch-api/service-worker/run.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +. test/setup/server.sh + +npx webpack --config $(dirname "$0")/webpack.config.js && +npx mocha-headless-chrome -f http://127.0.0.1:8000/$(dirname $0)/index.html diff --git a/test/fetch-api/service-worker/sw.js b/test/fetch-api/service-worker/sw.js new file mode 100644 index 0000000..bc1171a --- /dev/null +++ b/test/fetch-api/service-worker/sw.js @@ -0,0 +1,32 @@ +/* eslint-disable no-undef */ +import fetch, { Request, Response, Headers } from '../../..' + +const logChannel = new BroadcastChannel('sw-logger') + +// Redirect all logs to top window +console.log = (...args) => logChannel.postMessage(args) + +importScripts('../../../node_modules/mocha/mocha.js') +importScripts('../../../node_modules/chai/chai.js') +importScripts('./sw.reporter.js') + +importScripts('../api.spec.js') +importScripts('../../module-system/module.spec.js') + +globalThis.expect = chai.expect +globalThis.fetch = fetch +globalThis.Request = Request +globalThis.Response = Response +globalThis.Headers = Headers + +mocha.setup({ + ui: 'bdd', + reporter: SWReporter +}) + +describe('Browser:Fetch:ServiceWorker', () => { + addFetchSuite() + addNativeSuite({ fetch }) +}) + +mocha.run() diff --git a/test/fetch-api/service-worker/sw.reporter.js b/test/fetch-api/service-worker/sw.reporter.js new file mode 100644 index 0000000..ba5aad1 --- /dev/null +++ b/test/fetch-api/service-worker/sw.reporter.js @@ -0,0 +1,75 @@ +/** + * A SW Reporter created to be compatible with mocha-headless-chrome's repoter + * See: https://github.com/direct-adv-interfaces/mocha-headless-chrome/blob/273d9b8bc7445ea1196b10ad0eaf0a8bce6cbd5f/lib/runner.js#L68 + */ + +const { Spec } = Mocha.reporters +const { + EVENT_RUN_END, + EVENT_TEST_FAIL, + EVENT_TEST_PASS, + EVENT_TEST_PENDING +} = Mocha.Runner.constants + +function SWReporter (runner, options) { + Spec.call(this, runner, options) + + const all = [] + const passes = [] + const failures = [] + const pending = [] + + const error = (err) => { + if (!err) return {} + + const res = {} + Object.getOwnPropertyNames(err).forEach((key) => (res[key] = err[key])) + return res + } + + const clean = (test) => ({ + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: error(test.err) + }) + + const getResult = (stats) => ({ + result: { + stats: { + tests: all.length, + passes: passes.length, + pending: pending.length, + failures: failures.length, + start: stats.start.toISOString(), + end: stats.end.toISOString(), + duration: stats.duration + }, + tests: all.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + } + }) + + runner + .on(EVENT_TEST_PASS, (test) => { + passes.push(test) + all.push(test) + }) + .on(EVENT_TEST_FAIL, (test) => { + failures.push(test) + all.push(test) + }) + .on(EVENT_TEST_PENDING, (test) => { + pending.push(test) + all.push(test) + }) + .once(EVENT_RUN_END, () => { + const result = getResult(runner.stats) + const channel = new BroadcastChannel('sw-result') + channel.postMessage(JSON.stringify(result)) + }) +} + +Mocha.utils.inherits(SWReporter, Spec) diff --git a/test/fetch-api/service-worker/webpack.config.js b/test/fetch-api/service-worker/webpack.config.js new file mode 100644 index 0000000..58cc8d4 --- /dev/null +++ b/test/fetch-api/service-worker/webpack.config.js @@ -0,0 +1,12 @@ +const path = require('path') + +module.exports = { + target: 'webworker', + mode: 'none', + entry: path.join(__dirname, 'sw.js'), + output: { + path: __dirname, + filename: 'sw.bundle.js' + }, + stats: 'none' +}