From 1db04bb06d7b6c4cef978ed696587e7201423816 Mon Sep 17 00:00:00 2001 From: Julien Marcou Date: Thu, 21 Jul 2022 17:58:31 +0200 Subject: [PATCH] Fix LoaderResource tests (#8518) --- package-lock.json | 235 +++++- package.json | 3 +- packages/loaders/test/AsyncQueue.tests.ts | 2 +- packages/loaders/test/LoaderResource.test.ts | 670 ----------------- packages/loaders/test/LoaderResource.tests.ts | 705 ++++++++++++++++++ packages/loaders/test/fixtureData.ts | 8 - packages/ticker/test/Ticker.tests.ts | 6 +- 7 files changed, 936 insertions(+), 693 deletions(-) delete mode 100644 packages/loaders/test/LoaderResource.test.ts create mode 100644 packages/loaders/test/LoaderResource.tests.ts delete mode 100644 packages/loaders/test/fixtureData.ts diff --git a/package-lock.json b/package-lock.json index 5223b1a02a..5e3dd73caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,9 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-typescript": "^6.0.0", - "@types/chai": "^4.3.1", "@types/css-font-loading-module": "^0.0.7", "@types/jest": "^26.0.0", + "@types/sinon": "^10.0.13", "@webdoc/cli": "^1.5.5", "copyfiles": "^2.1.0", "cross-env": "^5.2.0", @@ -46,6 +46,7 @@ "rollup-plugin-string": "^3.0.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript": "^1.0.1", + "sinon": "^14.0.0", "ts-jest": "^26.0.0", "ts-node": "^9.0.0", "tsconfig-paths": "^3.10.1", @@ -6294,6 +6295,23 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@sinonjs/samsam": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "node_modules/@szmarczak/http-timer": { "version": "1.1.2", "dev": true, @@ -6366,11 +6384,6 @@ "magic-string": "^0.25.0" } }, - "node_modules/@types/chai": { - "version": "4.3.1", - "dev": true, - "license": "MIT" - }, "node_modules/@types/color-name": { "version": "1.1.1", "dev": true, @@ -6496,6 +6509,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/sinon": { + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -10008,6 +10036,15 @@ "wrappy": "1" } }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -17904,6 +17941,12 @@ "verror": "1.10.0" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "node_modules/keyv": { "version": "3.1.0", "dev": true, @@ -19244,6 +19287,19 @@ "dev": true, "license": "MIT" }, + "node_modules/nise": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, "node_modules/node-abi": { "version": "3.22.0", "license": "MIT", @@ -20579,6 +20635,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/path-type": { "version": "3.0.0", "dev": true, @@ -22109,6 +22174,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sinon": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -29614,6 +29727,23 @@ "@sinonjs/commons": "^1.7.0" } }, + "@sinonjs/samsam": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", + "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "@szmarczak/http-timer": { "version": "1.1.2", "dev": true, @@ -29676,10 +29806,6 @@ "magic-string": "^0.25.0" } }, - "@types/chai": { - "version": "4.3.1", - "dev": true - }, "@types/color-name": { "version": "1.1.1", "dev": true @@ -29791,6 +29917,21 @@ "version": "6.0.3", "dev": true }, + "@types/sinon": { + "version": "10.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", + "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -32153,6 +32294,12 @@ "wrappy": "1" } }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + }, "diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -37756,6 +37903,12 @@ "verror": "1.10.0" } }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "keyv": { "version": "3.1.0", "dev": true, @@ -38684,6 +38837,19 @@ "version": "1.0.5", "dev": true }, + "nise": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", + "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": ">=5", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, "node-abi": { "version": "3.22.0", "requires": { @@ -39579,6 +39745,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + }, "path-type": { "version": "3.0.0", "dev": true, @@ -40664,6 +40839,46 @@ } } }, + "sinon": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", + "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^9.1.2", + "@sinonjs/samsam": "^6.1.1", + "diff": "^5.0.0", + "nise": "^5.1.1", + "supports-color": "^7.2.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/package.json b/package.json index 1415eface2..cccd054164 100644 --- a/package.json +++ b/package.json @@ -56,9 +56,9 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-typescript": "^6.0.0", - "@types/chai": "^4.3.1", "@types/css-font-loading-module": "^0.0.7", "@types/jest": "^26.0.0", + "@types/sinon": "^10.0.13", "@webdoc/cli": "^1.5.5", "copyfiles": "^2.1.0", "cross-env": "^5.2.0", @@ -81,6 +81,7 @@ "rollup-plugin-string": "^3.0.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript": "^1.0.1", + "sinon": "^14.0.0", "ts-jest": "^26.0.0", "ts-node": "^9.0.0", "tsconfig-paths": "^3.10.1", diff --git a/packages/loaders/test/AsyncQueue.tests.ts b/packages/loaders/test/AsyncQueue.tests.ts index 141efbba0e..ee6814faae 100644 --- a/packages/loaders/test/AsyncQueue.tests.ts +++ b/packages/loaders/test/AsyncQueue.tests.ts @@ -366,7 +366,7 @@ describe('async', () => }; }); - it.skip('pause', (done: () => void) => + it('pause', (done: () => void) => { const callOrder: Array = []; const taskTimeout = 80; diff --git a/packages/loaders/test/LoaderResource.test.ts b/packages/loaders/test/LoaderResource.test.ts deleted file mode 100644 index 713cd1db91..0000000000 --- a/packages/loaders/test/LoaderResource.test.ts +++ /dev/null @@ -1,670 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck -/* eslint-disable @typescript-eslint/no-unused-expressions */ -import sinon from 'sinon'; -import type { IResourceMetadata } from '@pixi/loaders'; -import { LoaderResource } from '@pixi/loaders'; -import { expect } from 'chai'; -import { fixtureData } from './fixtures/data'; - -describe('LoaderResource', () => -{ - let request: any; - let res: any; - let xhr: any; - let clock: any; - const name = 'test-resource'; - - before(() => - { - xhr = sinon.useFakeXMLHttpRequest(); - xhr.onCreate = (req: any) => - { - request = req; - }; - clock = sinon.useFakeTimers(); - }); - - after(() => - { - xhr.restore(); - clock.restore(); - }); - - beforeEach(() => - { - res = new LoaderResource(name, fixtureData.url); - request = null; - }); - - it('should construct properly with only a URL passed', () => - { - expect(res).to.have.property('name', name); - expect(res).to.have.property('type', LoaderResource.TYPE.UNKNOWN); - expect(res).to.have.property('url', fixtureData.url); - expect(res).to.have.property('data', null); - expect(res).to.have.property('crossOrigin', undefined); - expect(res).to.have.property('loadType', LoaderResource.LOAD_TYPE.XHR); - expect(res).to.have.property('xhrType', undefined); - expect(res).to.have.property('metadata').that.is.eql({}); - expect(res).to.have.property('error', null); - expect(res).to.have.property('xhr', null); - - expect(res).to.have.property('isDataUrl', false); - expect(res).to.have.property('isComplete', false); - expect(res).to.have.property('isLoading', false); - }); - - it('should construct properly with options passed', () => - { - const meta = { some: 'thing' }; - const res = new LoaderResource(name, fixtureData.url, { - crossOrigin: true, - loadType: LoaderResource.LOAD_TYPE.IMAGE, - xhrType: LoaderResource.XHR_RESPONSE_TYPE.BLOB, - metadata: meta as IResourceMetadata, - }); - - expect(res).to.have.property('name', name); - expect(res).to.have.property('type', LoaderResource.TYPE.UNKNOWN); - expect(res).to.have.property('url', fixtureData.url); - expect(res).to.have.property('data', null); - expect(res).to.have.property('crossOrigin', 'anonymous'); - expect(res).to.have.property('loadType', LoaderResource.LOAD_TYPE.IMAGE); - expect(res).to.have.property('xhrType', LoaderResource.XHR_RESPONSE_TYPE.BLOB); - expect(res).to.have.property('metadata', meta); - expect(res).to.have.property('error', null); - expect(res).to.have.property('xhr', null); - - expect(res).to.have.property('isDataUrl', false); - expect(res).to.have.property('isComplete', false); - expect(res).to.have.property('isLoading', false); - }); - - describe('#complete', () => - { - it('should emit the `complete` event', () => - { - const spy = sinon.spy(); - - res.onComplete.add(spy); - - res.complete(); - - expect(spy).to.have.been.calledWith(res); - }); - - it('should remove events from the data element', () => - { - const data = { - addEventListener: () => { /* empty */ }, - removeEventListener: () => { /* empty */ }, - }; - const mock = sinon.mock(data); - - mock.expects('removeEventListener').once().withArgs('error'); - mock.expects('removeEventListener').once().withArgs('load'); - mock.expects('removeEventListener').once().withArgs('progress'); - mock.expects('removeEventListener').once().withArgs('canplaythrough'); - - res.data = data; - res.complete(); - - mock.verify(); - }); - - it('should remove events from the xhr element', () => - { - const data: any = { - addEventListener: () => { /* empty */ }, - removeEventListener: () => { /* empty */ }, - }; - const mock = sinon.mock(data); - - mock.expects('removeEventListener').once().withArgs('error'); - mock.expects('removeEventListener').once().withArgs('timeout'); - mock.expects('removeEventListener').once().withArgs('abort'); - mock.expects('removeEventListener').once().withArgs('progress'); - mock.expects('removeEventListener').once().withArgs('load'); - - res.xhr = data; - res.complete(); - - mock.verify(); - }); - }); - - describe('#abort', () => - { - it('should abort in-flight XHR requests', () => - { - res.load(); - - res.xhr.abort = sinon.spy(); - - res.abort(undefined); - - expect(res.xhr.abort).to.have.been.calledOnce; - }); - - it('should abort in-flight XDR requests'); - - it('should abort in-flight Image requests', () => - { - res.data = new Image(); - res.data.src = fixtureData.url; - - expect(res.data.src).to.equal(fixtureData.url); - - res.abort(undefined); - - expect(res.data.src).to.equal(LoaderResource.EMPTY_GIF); - }); - - it('should abort in-flight Video requests', () => - { - res.data = document.createElement('video'); - res.data.appendChild(document.createElement('source')); - - expect(res.data.firstChild).to.exist; - - res.abort(undefined); - - expect(res.data.firstChild).to.not.exist; - }); - - it('should abort in-flight Audio requests', () => - { - res.data = document.createElement('audio'); - res.data.appendChild(document.createElement('source')); - - expect(res.data.firstChild).to.exist; - - res.abort(undefined); - - expect(res.data.firstChild).to.not.exist; - }); - }); - - describe('#load', () => - { - it('should emit the start event', () => - { - const spy = sinon.spy(); - - res.onStart.add(spy); - - res.load(); - - expect(request).to.exist; - expect(spy).to.have.been.calledWith(res); - }); - - it('should emit the complete event', () => - { - const spy = sinon.spy(); - - res.onComplete.add(spy); - - res.load(); - - request.respond(200, fixtureData.dataJsonHeaders, fixtureData.dataJson); - - expect(request).to.exist; - expect(spy).to.have.been.calledWith(res); - }); - - it('should not load and emit a complete event if complete is called before load', () => - { - const spy = sinon.spy(); - - res.onComplete.add(spy); - - res.complete(); - res.load(); - - expect(request).not.to.exist; - expect(spy).to.have.been.calledWith(res); - }); - - it('should throw an error if complete is called twice', () => - { - function fn() - { - res.complete(); - } - - expect(fn).to.not.throw(Error); - expect(fn).to.throw(Error); - }); - - it('should load using a data url', (done) => - { - const res = new LoaderResource(name, fixtureData.dataUrlGif); - - res.onComplete.add(() => - { - expect(res).to.have.property('data').instanceOf(Image) - .and.is.an.instanceOf(HTMLImageElement) - .and.have.property('src', fixtureData.dataUrlGif); - - done(); - }); - - res.load(); - }); - - it('should load using a svg data url', (done) => - { - const res = new LoaderResource(name, fixtureData.dataUrlSvg); - - res.onComplete.add(() => - { - expect(res).to.have.property('data').instanceOf(Image) - .and.is.an.instanceOf(HTMLImageElement) - .and.have.property('src', fixtureData.dataUrlSvg); - - done(); - }); - - res.load(); - }); - - it('should load using XHR', (done) => - { - res.onComplete.add(() => - { - expect(res).to.have.property('data', fixtureData.dataJson); - done(); - }); - - res.load(); - - expect(request).to.exist; - - request.respond(200, fixtureData.dataJsonHeaders, fixtureData.dataJson); - }); - - it('should load using Image', () => - { - const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.IMAGE }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(Image) - .and.is.an.instanceOf(HTMLImageElement) - .and.have.property('src', fixtureData.url); - }); - - it('should load using Audio', () => - { - const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.AUDIO }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(HTMLAudioElement); - - expect(res.data.children).to.have.length(1); - expect(res.data.children[0]).to.have.property('src', fixtureData.url); - }); - - it('should load using Video', () => - { - const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.VIDEO }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(HTMLVideoElement); - - expect(res.data.children).to.have.length(1); - expect(res.data.children[0]).to.have.property('src', fixtureData.url); - }); - - it('should used the passed element for loading', () => - { - const img = new Image(); - const spy = sinon.spy(img, 'addEventListener'); - const res = new LoaderResource(name, fixtureData.url, { - loadType: LoaderResource.LOAD_TYPE.IMAGE, - metadata: { loadElement: img }, - }); - - res.load(); - - expect(spy).to.have.been.calledThrice; - expect(img).to.have.property('src', fixtureData.url); - - spy.restore(); - }); - - it('should used the passed element for loading, and skip assigning src', () => - { - const img = new Image(); - const spy = sinon.spy(img, 'addEventListener'); - const res = new LoaderResource(name, fixtureData.url, { - loadType: LoaderResource.LOAD_TYPE.IMAGE, - metadata: { loadElement: img, skipSource: true }, - }); - - res.load(); - - expect(spy).to.have.been.calledThrice; - expect(img).to.have.property('src', ''); - - spy.restore(); - }); - - it('should set withCredentials for XHR when crossOrigin specified', () => - { - const res = new LoaderResource(name, fixtureData.url, { - loadType: LoaderResource.LOAD_TYPE.XHR, - }); - - res.crossOrigin = 'use-credentials'; - - res.load(); - - expect(request.withCredentials).to.equal(true); - }); - }); - - describe('#load with timeout', () => - { - it('should abort XHR loads', (done) => - { - const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.XHR, timeout: 100 }); - - res.onComplete.add(() => - { - expect(res).to.have.property('error').instanceOf(Error); - expect(res).to.have.property('data').equal(null); - done(); - }); - - res.load(); - - expect(request).to.exist; - request.triggerTimeout(); - }); - - it('should abort Image loads', (done) => - { - const res = new LoaderResource(name, fixtureData.url, - { loadType: LoaderResource.LOAD_TYPE.IMAGE, timeout: 1000 }); - - res.onComplete.add(() => - { - expect(res).to.have.property('error').instanceOf(Error); - - expect(res).to.have.property('data').instanceOf(Image) - .and.is.an.instanceOf(HTMLImageElement) - .and.have.property('src', LoaderResource.EMPTY_GIF); - done(); - }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(Image) - .and.is.an.instanceOf(HTMLImageElement) - .and.have.property('src', fixtureData.url); - - clock.tick(1100); - }); - - it('should abort Audio loads', (done) => - { - const res = new LoaderResource(name, fixtureData.url, - { loadType: LoaderResource.LOAD_TYPE.AUDIO, timeout: 1000 }); - - res.onComplete.add(() => - { - expect(res).to.have.property('error').instanceOf(Error); - expect(res.data.children).to.have.length(0); - done(); - }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(HTMLAudioElement); - expect(res.data.children).to.have.length(1); - expect(res.data.children[0]).to.have.property('src', fixtureData.url); - - clock.tick(1100); - }); - - it('should abort Video loads', (done) => - { - const res = new LoaderResource(name, fixtureData.url, - { loadType: LoaderResource.LOAD_TYPE.VIDEO, timeout: 1000 }); - - res.onComplete.add(() => - { - expect(res).to.have.property('error').instanceOf(Error); - expect(res.data.children).to.have.length(0); - done(); - }); - - res.load(); - - expect(request).to.not.exist; - - expect(res).to.have.property('data').instanceOf(HTMLVideoElement); - expect(res.data.children).to.have.length(1); - expect(res.data.children[0]).to.have.property('src', fixtureData.url); - - clock.tick(1100); - }); - }); - - describe('#load inside cordova', () => - { - beforeEach(() => - { - xhr.status = 0; - }); - - it('should load resource even if the status is 0', () => - { - xhr.responseText = 'I am loaded resource'; - - res.xhr = xhr; - res['_xhrOnLoad'](); - - expect(res.isComplete).to.equal(true); - }); - - it('should load resource with array buffer data', () => - { - xhr.responseType = LoaderResource.XHR_RESPONSE_TYPE.BUFFER; - - res.xhr = xhr; - res['_xhrOnLoad'](); - - expect(res.isComplete).to.equal(true); - }); - }); - - describe('#_determineCrossOrigin', () => - { - it('should properly detect same-origin requests (#1)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com', - { hostname: 'google.com', port: '', protocol: 'https:' } - )).to.equal(''); - }); - - it('should properly detect same-origin requests (#2)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com:443', - { hostname: 'google.com', port: '', protocol: 'https:' } - )).to.equal(''); - }); - - it('should properly detect same-origin requests (#3)', () => - { - expect(res._determineCrossOrigin( - 'http://www.google.com:5678', - { hostname: 'www.google.com', port: '5678', protocol: 'http:' } - )).to.equal(''); - }); - - it('should properly detect cross-origin requests (#1)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com', - { hostname: 'google.com', port: '123', protocol: 'https:' } - )).to.equal('anonymous'); - }); - - it('should properly detect cross-origin requests (#2)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com', - { hostname: 'google.com', port: '', protocol: 'http:' } - )).to.equal('anonymous'); - }); - - it('should properly detect cross-origin requests (#3)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com', - { hostname: 'googles.com', port: '', protocol: 'https:' } - )).to.equal('anonymous'); - }); - - it('should properly detect cross-origin requests (#4)', () => - { - expect(res._determineCrossOrigin( - 'https://google.com', - { hostname: 'www.google.com', port: '123', protocol: 'https:' } - )).to.equal('anonymous'); - }); - it('should properly detect cross-origin requests (#5) - sandboxed iframe', () => - { - const originalOrigin = window.origin; - - // Set origin to 'null' to simulate sandboxed iframe without 'allow-same-origin' attribute - (window as any).origin = 'null'; - expect(res._determineCrossOrigin( - 'http://www.google.com:5678', - { hostname: 'www.google.com', port: '5678', protocol: 'http:' } - )).to.equal('anonymous'); - // Restore origin to prevent test leakage. - (window as any).origin = originalOrigin; - }); - }); - - describe('#_getExtension', () => - { - it('should return the proper extension', () => - { - res.url = 'http://www.google.com/image.png'; - expect(res['_getExtension']()).to.equal('png'); - - res.url = 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'; - expect(res['_getExtension']()).to.equal('wmv'); - - res.url = 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/data.txt'; - expect(res['_getExtension']()).to.equal('txt'); - - res.url = 'http://nowhere.me/image.jpg?query=true&string=false&name=real'; - expect(res['_getExtension']()).to.equal('jpg'); - - res.url = 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json'; - expect(res['_getExtension']()).to.equal('jpeg'); - - res.url = 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json'; - expect(res['_getExtension']()).to.equal('jpeg'); - - res.url = 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json#/derp.mp3'; - expect(res['_getExtension']()).to.equal('jpeg'); - - res.url = 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; - expect(res['_getExtension']()).to.equal('jpeg'); - - res.url = 'http://nowhere.me/image.jpeg#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; // eslint-disable-line max-len - expect(res['_getExtension']()).to.equal('jpeg'); - - res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); - res.url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY2BgYAAAAAQAAVzN/2kAAAAASUVORK5CYII='; // eslint-disable-line max-len - expect(res['_getExtension']()).to.equal('png'); - }); - }); - - describe('#_createSource', () => - { - it('Should return the correct src url', () => - { - res.url = 'http://www.google.com/audio.mp3'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'; - expect(res['_createSource']('video', res.url)).to.have.property('src', res.url); - - res.url = 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/audio.mp3'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://nowhere.me/audio.mp3?query=true&string=false&name=real'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res.url = 'http://nowhere.me/audio.mp3#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; // eslint-disable-line max-len - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - - res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); - res.url = 'data:audio/wave;base64,UklGRjIAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAAABmYWN0BAAAAAAAAABkYXRhAAAAAA=='; // eslint-disable-line max-len - expect(res['_createSource']('audio', res.url)).to.have.property('src', res.url); - }); - - it('Should correctly auto-detect the mime type', () => - { - res.url = 'http://www.google.com/audio.mp3'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'; - expect(res['_createSource']('video', res.url)).to.have.property('type', 'video/wmv'); - - res.url = 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/audio.mp3'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://nowhere.me/audio.mp3?query=true&string=false&name=real'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res.url = 'http://nowhere.me/audio.mp3#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'; // eslint-disable-line max-len - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/mp3'); - - res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); - res.url = 'data:audio/wave;base64,UklGRjIAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAAABmYWN0BAAAAAAAAABkYXRhAAAAAA=='; // eslint-disable-line max-len - expect(res['_createSource']('audio', res.url)).to.have.property('type', 'audio/wave'); - }); - }); -}); diff --git a/packages/loaders/test/LoaderResource.tests.ts b/packages/loaders/test/LoaderResource.tests.ts new file mode 100644 index 0000000000..bf32b77722 --- /dev/null +++ b/packages/loaders/test/LoaderResource.tests.ts @@ -0,0 +1,705 @@ +import type { IResourceMetadata } from '@pixi/loaders'; +import type { SinonFakeXMLHttpRequest, SinonFakeXMLHttpRequestStatic, SinonFakeTimers } from 'sinon'; +import { LoaderResource } from '@pixi/loaders'; +import sinon from 'sinon'; +import { fixtureData } from './fixtures/data'; + +describe('LoaderResource', () => +{ + let request: SinonFakeXMLHttpRequest; + let res: LoaderResource; + let xhr: SinonFakeXMLHttpRequestStatic; + let clock: SinonFakeTimers; + const name = 'test-resource'; + + beforeAll(() => + { + xhr = sinon.useFakeXMLHttpRequest(); + xhr.onCreate = (req) => + { + request = req; + }; + clock = sinon.useFakeTimers(); + }); + + afterAll(() => + { + xhr.restore(); + clock.restore(); + }); + + beforeEach(() => + { + res = new LoaderResource(name, fixtureData.url); + request = undefined; + }); + + it('should construct properly with only a URL passed', () => + { + expect(res).toHaveProperty('name', name); + expect(res).toHaveProperty('type', LoaderResource.TYPE.UNKNOWN); + expect(res).toHaveProperty('url', fixtureData.url); + expect(res).toHaveProperty('data', null); + expect(res).toHaveProperty('crossOrigin', undefined); + expect(res).toHaveProperty('loadType', LoaderResource.LOAD_TYPE.IMAGE); + expect(res).toHaveProperty('xhrType', undefined); + expect(res).toHaveProperty('metadata'); + expect(res.metadata).toEqual({}); + expect(res).toHaveProperty('error', null); + expect(res).toHaveProperty('xhr', null); + + expect(res).toHaveProperty('isDataUrl', false); + expect(res).toHaveProperty('isComplete', false); + expect(res).toHaveProperty('isLoading', false); + }); + + it('should construct properly with options passed', () => + { + const meta = { some: 'thing' }; + const res = new LoaderResource(name, fixtureData.url, { + crossOrigin: true, + loadType: LoaderResource.LOAD_TYPE.IMAGE, + xhrType: LoaderResource.XHR_RESPONSE_TYPE.BLOB, + metadata: meta as IResourceMetadata, + }); + + expect(res).toHaveProperty('name', name); + expect(res).toHaveProperty('type', LoaderResource.TYPE.UNKNOWN); + expect(res).toHaveProperty('url', fixtureData.url); + expect(res).toHaveProperty('data', null); + expect(res).toHaveProperty('crossOrigin', 'anonymous'); + expect(res).toHaveProperty('loadType', LoaderResource.LOAD_TYPE.IMAGE); + expect(res).toHaveProperty('xhrType', LoaderResource.XHR_RESPONSE_TYPE.BLOB); + expect(res).toHaveProperty('metadata', meta); + expect(res).toHaveProperty('error', null); + expect(res).toHaveProperty('xhr', null); + + expect(res).toHaveProperty('isDataUrl', false); + expect(res).toHaveProperty('isComplete', false); + expect(res).toHaveProperty('isLoading', false); + }); + + describe('#complete', () => + { + it('should emit the `complete` event', () => + { + const spy = jest.fn(); + + res.onComplete.add(spy); + + res.complete(); + + expect(spy).toHaveBeenCalledWith(res); + }); + + it('should remove events from the data element', () => + { + const data = { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + jest.spyOn(data, 'removeEventListener'); + + res.data = data; + res.complete(); + + expect(data.removeEventListener.mock.calls[0][0]).toEqual('error'); + expect(data.removeEventListener.mock.calls[1][0]).toEqual('load'); + expect(data.removeEventListener.mock.calls[2][0]).toEqual('progress'); + expect(data.removeEventListener.mock.calls[3][0]).toEqual('canplaythrough'); + + jest.clearAllMocks(); + }); + + it('should remove events from the xhr element', () => + { + const data = { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + jest.spyOn(data, 'removeEventListener'); + + (res as any).xhr = data; + res.complete(); + + expect(data.removeEventListener.mock.calls[0][0]).toEqual('error'); + expect(data.removeEventListener.mock.calls[1][0]).toEqual('timeout'); + expect(data.removeEventListener.mock.calls[2][0]).toEqual('abort'); + expect(data.removeEventListener.mock.calls[3][0]).toEqual('progress'); + expect(data.removeEventListener.mock.calls[4][0]).toEqual('load'); + + jest.clearAllMocks(); + }); + }); + + describe('#abort', () => + { + it('should abort in-flight XHR requests', () => + { + res.load(); + + (res as any).xhr = { + abort: jest.fn(), + }; + + res.abort(undefined); + + expect(res.xhr.abort).toHaveBeenCalledOnce(); + }); + + it('should abort in-flight Image requests', () => + { + res.data = new Image(); + res.data.src = fixtureData.url; + + expect(res.data.src).toEqual(fixtureData.url); + + res.abort(undefined); + + expect(res.data.src).toEqual(LoaderResource.EMPTY_GIF); + }); + + it('should abort in-flight Video requests', () => + { + res.data = document.createElement('video'); + res.data.appendChild(document.createElement('source')); + + expect(res.data.firstChild).toBeDefined(); + + res.abort(undefined); + + expect(res.data.firstChild).toBeNull(); + }); + + it('should abort in-flight Audio requests', () => + { + res.data = document.createElement('audio'); + res.data.appendChild(document.createElement('source')); + + expect(res.data.firstChild).toBeDefined(); + + res.abort(undefined); + + expect(res.data.firstChild).toBeNull(); + }); + }); + + describe('#load', () => + { + it('should emit the start event', () => + { + const res = new LoaderResource(name, fixtureData.baseUrl); + + const spy = jest.fn(); + + res.onStart.add(spy); + + res.load(); + + expect(request).toBeDefined(); + expect(spy).toHaveBeenCalledWith(res); + }); + + it('should emit the complete event', () => + { + const res = new LoaderResource(name, fixtureData.baseUrl); + + const spy = jest.fn(); + + res.onComplete.add(spy); + + res.load(); + + request.respond(200, fixtureData.dataJsonHeaders, fixtureData.dataJson); + + expect(request).toBeDefined(); + expect(spy).toHaveBeenCalledWith(res); + }); + + it('should not load and emit a complete event if complete is called before load', () => + { + const spy = jest.fn(); + + res.onComplete.add(spy); + + res.complete(); + res.load(); + + expect(request).toBeUndefined(); + expect(spy).toHaveBeenCalledWith(res); + }); + + it('should throw an error if complete is called twice', () => + { + function fn() + { + res.complete(); + } + + expect(fn).not.toThrow(Error); + expect(fn).toThrow(Error); + }); + + it('should load using a data url', (done) => + { + const res = new LoaderResource(name, fixtureData.dataUrlGif); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(Image); + expect(res.data).toBeInstanceOf(HTMLImageElement); + expect(res.data).toHaveProperty('src'); + expect(res.data.src).toEqual(fixtureData.dataUrlGif); + + done(); + }); + + res.load(); + }); + + it('should load using a svg data url', (done) => + { + const res = new LoaderResource(name, fixtureData.dataUrlSvg); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(Image); + expect(res.data).toBeInstanceOf(HTMLImageElement); + expect(res.data).toHaveProperty('src'); + expect(res.data.src).toEqual(fixtureData.dataUrlSvg); + + done(); + }); + + res.load(); + }); + + it('should load using XHR', (done) => + { + const res = new LoaderResource(name, fixtureData.baseUrl); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('data'); + expect(res.data).toEqual(fixtureData.dataJson); + done(); + }); + + res.load(); + + expect(request).toBeDefined(); + + request.respond(200, fixtureData.dataJsonHeaders, fixtureData.dataJson); + }); + + it('should load using Image', () => + { + const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.IMAGE }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(Image); + expect(res.data).toBeInstanceOf(HTMLImageElement); + expect(res.data).toHaveProperty('src'); + expect(res.data.src).toEqual(fixtureData.url); + }); + + it('should load using Audio', () => + { + const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.AUDIO }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(HTMLAudioElement); + + expect(res.data.children).toHaveLength(1); + expect(res.data.children[0]).toHaveProperty('src', fixtureData.url); + }); + + it('should load using Video', () => + { + const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.VIDEO }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(HTMLVideoElement); + + expect(res.data.children).toHaveLength(1); + expect(res.data.children[0]).toHaveProperty('src', fixtureData.url); + }); + + it('should used the passed element for loading', () => + { + const img = new Image(); + const spy = jest.spyOn(img, 'addEventListener'); + const res = new LoaderResource(name, fixtureData.url, { + loadType: LoaderResource.LOAD_TYPE.IMAGE, + metadata: { loadElement: img }, + }); + + res.load(); + + expect(spy).toHaveBeenCalledTimes(3); + expect(img).toHaveProperty('src', fixtureData.url); + + jest.resetAllMocks(); + }); + + it('should used the passed element for loading, and skip assigning src', () => + { + const img = new Image(); + const spy = jest.spyOn(img, 'addEventListener'); + const res = new LoaderResource(name, fixtureData.url, { + loadType: LoaderResource.LOAD_TYPE.IMAGE, + metadata: { loadElement: img, skipSource: true }, + }); + + res.load(); + + expect(spy).toHaveBeenCalledTimes(3); + expect(img).toHaveProperty('src', ''); + + jest.resetAllMocks(); + }); + + it('should set withCredentials for XHR when crossOrigin specified', () => + { + const res = new LoaderResource(name, fixtureData.url, { + loadType: LoaderResource.LOAD_TYPE.XHR, + }); + + res.crossOrigin = 'use-credentials'; + + res.load(); + + expect(request.withCredentials).toEqual(true); + }); + }); + + describe('#load with timeout', () => + { + it('should abort XHR loads', (done) => + { + const res = new LoaderResource(name, fixtureData.url, { loadType: LoaderResource.LOAD_TYPE.XHR, timeout: 100 }); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('error'); + expect(res.error).toBeInstanceOf(Error); + expect(res).toHaveProperty('data'); + expect(res.data).toEqual(null); + done(); + }); + + res.load(); + + expect(request).toBeDefined(); + + clock.tick(200); + }); + + it('should abort Image loads', (done) => + { + const res = new LoaderResource(name, fixtureData.url, + { loadType: LoaderResource.LOAD_TYPE.IMAGE, timeout: 1000 }); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('error'); + expect(res.error).toBeInstanceOf(Error); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(Image); + expect(res.data).toBeInstanceOf(HTMLImageElement); + expect(res.data).toHaveProperty('src'); + expect(res.data.src).toEqual(LoaderResource.EMPTY_GIF); + + done(); + }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(Image); + expect(res.data).toBeInstanceOf(HTMLImageElement); + expect(res.data).toHaveProperty('src'); + expect(res.data.src).toEqual(fixtureData.url); + + clock.tick(1100); + }); + + it('should abort Audio loads', (done) => + { + const res = new LoaderResource(name, fixtureData.url, + { loadType: LoaderResource.LOAD_TYPE.AUDIO, timeout: 1000 }); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('error'); + expect(res.error).toBeInstanceOf(Error); + expect(res.data.children).toHaveLength(0); + done(); + }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(HTMLAudioElement); + expect(res.data.children).toHaveLength(1); + expect(res.data.children[0]).toHaveProperty('src', fixtureData.url); + + clock.tick(1100); + }); + + it('should abort Video loads', (done) => + { + const res = new LoaderResource(name, fixtureData.url, + { loadType: LoaderResource.LOAD_TYPE.VIDEO, timeout: 1000 }); + + res.onComplete.add(() => + { + expect(res).toHaveProperty('error'); + expect(res.error).toBeInstanceOf(Error); + expect(res.data.children).toHaveLength(0); + done(); + }); + + res.load(); + + expect(request).toBeUndefined(); + + expect(res).toHaveProperty('data'); + expect(res.data).toBeInstanceOf(HTMLVideoElement); + expect(res.data.children).toHaveLength(1); + expect(res.data.children[0]).toHaveProperty('src'); + expect(res.data.children[0].src).toEqual(fixtureData.url); + + clock.tick(1100); + }); + }); + + describe('#load inside cordova', () => + { + beforeEach(() => + { + (xhr as any).status = 0; + }); + + it('should load resource even if the status is 0', () => + { + (xhr as any).responseText = 'I am loaded resource'; + + res.xhr = xhr as any; + res['_xhrOnLoad'](); + + expect(res.isComplete).toEqual(true); + }); + + it('should load resource with array buffer data', () => + { + (xhr as any).responseType = LoaderResource.XHR_RESPONSE_TYPE.BUFFER; + + res.xhr = xhr as any; + res['_xhrOnLoad'](); + + expect(res.isComplete).toEqual(true); + }); + }); + + describe('#_determineCrossOrigin', () => + { + it('should properly detect same-origin requests (#1)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com', + { hostname: 'google.com', port: '', protocol: 'https:' } + )).toEqual(''); + }); + + it('should properly detect same-origin requests (#2)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com:443', + { hostname: 'google.com', port: '', protocol: 'https:' } + )).toEqual(''); + }); + + it('should properly detect same-origin requests (#3)', () => + { + expect(res._determineCrossOrigin( + 'http://www.google.com:5678', + { hostname: 'www.google.com', port: '5678', protocol: 'http:' } + )).toEqual(''); + }); + + it('should properly detect cross-origin requests (#1)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com', + { hostname: 'google.com', port: '123', protocol: 'https:' } + )).toEqual('anonymous'); + }); + + it('should properly detect cross-origin requests (#2)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com', + { hostname: 'google.com', port: '', protocol: 'http:' } + )).toEqual('anonymous'); + }); + + it('should properly detect cross-origin requests (#3)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com', + { hostname: 'googles.com', port: '', protocol: 'https:' } + )).toEqual('anonymous'); + }); + + it('should properly detect cross-origin requests (#4)', () => + { + expect(res._determineCrossOrigin( + 'https://google.com', + { hostname: 'www.google.com', port: '123', protocol: 'https:' } + )).toEqual('anonymous'); + }); + it('should properly detect cross-origin requests (#5) - sandboxed iframe', () => + { + const originalOrigin = window.origin; + + // Set origin to 'null' to simulate sandboxed iframe without 'allow-same-origin' attribute + (window as any).origin = 'null'; + expect(res._determineCrossOrigin( + 'http://www.google.com:5678', + { hostname: 'www.google.com', port: '5678', protocol: 'http:' } + )).toEqual('anonymous'); + // Restore origin to prevent test leakage. + (window as any).origin = originalOrigin; + }); + }); + + describe('#_getExtension', () => + { + it('should return the proper extension', () => + { + let res; + + res = new LoaderResource(name, 'http://www.google.com/image.png'); + expect(res['_getExtension']()).toEqual('png'); + + res = new LoaderResource(name, 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'); + expect(res['_getExtension']()).toEqual('wmv'); + + res = new LoaderResource(name, 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/data.txt'); + expect(res['_getExtension']()).toEqual('txt'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpg?query=true&string=false&name=real'); + expect(res['_getExtension']()).toEqual('jpg'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json'); + expect(res['_getExtension']()).toEqual('jpeg'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json'); + expect(res['_getExtension']()).toEqual('jpeg'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json#/derp.mp3'); + expect(res['_getExtension']()).toEqual('jpeg'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpeg?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); + expect(res['_getExtension']()).toEqual('jpeg'); + + res = new LoaderResource(name, 'http://nowhere.me/image.jpeg#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); // eslint-disable-line max-len + expect(res['_getExtension']()).toEqual('jpeg'); + + res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); + res = new LoaderResource(name, 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY2BgYAAAAAQAAVzN/2kAAAAASUVORK5CYII='); // eslint-disable-line max-len + expect(res['_getExtension']()).toEqual('png'); + }); + }); + + describe('#_createSource', () => + { + it('Should return the correct src url', () => + { + let res; + + res = new LoaderResource(name, 'http://www.google.com/audio.mp3'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'); + expect(res['_createSource']('video', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/audio.mp3'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=true&string=false&name=real'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); // eslint-disable-line max-len + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + + res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); + res = new LoaderResource(name, 'data:audio/wave;base64,UklGRjIAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAAABmYWN0BAAAAAAAAABkYXRhAAAAAA=='); // eslint-disable-line max-len + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('src', res.url); + }); + + it('Should correctly auto-detect the mime type', () => + { + let res; + + res = new LoaderResource(name, 'http://www.google.com/audio.mp3'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://domain.net/really/deep/path/that/goes/for/a/while/movie.wmv'); + expect(res['_createSource']('video', res.url, '')).toHaveProperty('type', 'video/wmv'); + + res = new LoaderResource(name, 'http://somewhere.io/path.with.dots/and_a-bunch_of.symbols/audio.mp3'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=true&string=false&name=real'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res = new LoaderResource(name, 'http://nowhere.me/audio.mp3#nothing-to-see-here?query=movie.wmv&file=data.json#/derp.mp3&?me=two'); // eslint-disable-line max-len + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/mp3'); + + res['_setFlag'](LoaderResource.STATUS_FLAGS.DATA_URL, true); + res = new LoaderResource(name, 'data:audio/wave;base64,UklGRjIAAABXQVZFZm10IBIAAAABAAEAQB8AAEAfAAABAAgAAABmYWN0BAAAAAAAAABkYXRhAAAAAA=='); // eslint-disable-line max-len + expect(res['_createSource']('audio', res.url, '')).toHaveProperty('type', 'audio/wave'); + }); + }); +}); diff --git a/packages/loaders/test/fixtureData.ts b/packages/loaders/test/fixtureData.ts deleted file mode 100644 index 2503b65b8c..0000000000 --- a/packages/loaders/test/fixtureData.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const fixtureData = { - url: 'http://localhost/file', - baseUrl: '/base/test/data', - dataUrlGif: 'data:image/gif;base64,R0lGODlhAQABAPAAAP8REf///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==', - dataUrlSvg: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSczMCcgaGVpZ2h0PSczMCc+PGNpcmNsZSBjeD0nMTUnIGN5PScxNScgcj0nMTAnIC8+PC9zdmc+', // eslint-disable-line max-len - dataJson: '[{ "id": 12, "comment": "Hey there" }]', - dataJsonHeaders: { 'Content-Type': 'application/json' }, -}; diff --git a/packages/ticker/test/Ticker.tests.ts b/packages/ticker/test/Ticker.tests.ts index 53a90ecd06..6be500fe1e 100644 --- a/packages/ticker/test/Ticker.tests.ts +++ b/packages/ticker/test/Ticker.tests.ts @@ -228,7 +228,7 @@ describe('Ticker', () => expect(length()).toEqual(len); }); - it.skip('should remove once listener in a stack', () => + it('should remove once listener in a stack', () => { const len = length(); const listener1 = jest.fn(); @@ -333,7 +333,7 @@ describe('Ticker', () => expect(length()).toEqual(len); }); - it.skip('should remove itself before, still calling new item', () => + it('should remove itself before, still calling new item', () => { const len = length(); const listener2 = jest.fn(); @@ -365,7 +365,7 @@ describe('Ticker', () => expect(length()).toEqual(len); }); - it.skip('should remove items before and after current priority', () => + it('should remove items before and after current priority', () => { const len = length(); const listener2 = jest.fn();