// @ts-nocheck import { expect } from '@open-wc/testing'; import * as sinon from 'sinon'; import { ajaxCache, pendingRequestStore, resetCacheSession, extendCacheOptions, validateCacheOptions, invalidateMatchingCache, isCurrentSessionId, } from '../src/cacheManager.js'; import Cache from '../src/Cache.js'; import PendingRequestStore from '../src/PendingRequestStore.js'; describe('cacheManager', () => { describe('ajaxCache', () => { it('is an instance of the Cache class', () => { expect(ajaxCache).to.be.instanceOf(Cache); }); }); describe('pendingRequestStore', () => { it('is an instance of the PendingRequestStore class', () => { expect(pendingRequestStore).to.be.instanceOf(PendingRequestStore); }); }); describe('resetCacheSession', () => { let ajaxCacheSpy; let pendingRequestStoreSpy; beforeEach(() => { ajaxCacheSpy = sinon.spy(ajaxCache, 'reset'); pendingRequestStoreSpy = sinon.spy(pendingRequestStore, 'reset'); }); afterEach(() => { ajaxCacheSpy.restore(); pendingRequestStoreSpy.restore(); }); it('throws an Error when no cacheId is passed', () => { try { resetCacheSession(); } catch (e) { expect(e).to.be.an.instanceOf(Error); } }); it('assigns the passed cacheId to the cacheSessionId', () => { // Arrange const cacheId = 'a-new-cache-id'; // Act resetCacheSession(cacheId); // Assert expect(ajaxCacheSpy.calledOnce).to.be.true; expect(pendingRequestStoreSpy.calledOnce).to.be.true; }); }); describe('extendCacheOptions', () => { // Arrange const DEFAULT_MAX_AGE = 1000 * 60 * 60; const invalidateUrls = ['https://f00.bar/', 'https://share.ware/']; const invalidateUrlsRegex = /f00/; it('returns object with default values', () => { // Act const { useCache, methods, maxAge, requestIdFunction, invalidateUrls: invalidateUrlsResult, invalidateUrlsRegex: invalidateUrlsRegexResult, contentTypes, maxResponseSize, maxCacheSize, } = extendCacheOptions({ invalidateUrls, invalidateUrlsRegex }); // Assert expect(useCache).to.be.false; expect(methods).to.eql(['get']); expect(maxAge).to.equal(DEFAULT_MAX_AGE); expect(typeof requestIdFunction).to.eql('function'); expect(invalidateUrlsResult).to.equal(invalidateUrls); expect(invalidateUrlsRegexResult).to.equal(invalidateUrlsRegex); expect(contentTypes).to.be.undefined; expect(maxResponseSize).to.be.undefined; expect(maxCacheSize).to.be.undefined; }); it('the DEFAULT_GET_REQUEST_ID function throws when called with no arguments', () => { // Arrange const { requestIdFunction } = extendCacheOptions({ invalidateUrls, invalidateUrlsRegex, }); // Act expect(requestIdFunction).to.throw(TypeError); }); it('the DEFAULT_GET_REQUEST_ID function returns a url when URLSearchParams cannot be serialized', () => { // Arrange const { requestIdFunction } = extendCacheOptions({ invalidateUrls, invalidateUrlsRegex, }); // Act const formattedUrl = requestIdFunction({ url: 'http://f00.bar/', params: {}, }); // Assert expect(formattedUrl).to.equal('http://f00.bar/'); }); it('the DEFAULT_GET_REQUEST_ID function returns a correctly formatted url with URLSearchParams', () => { // Arrange const { requestIdFunction } = extendCacheOptions({ invalidateUrls, invalidateUrlsRegex, }); // Act const formattedUrl = requestIdFunction({ url: 'http://f00.bar/', params: { f00: 'bar', bar: 'f00' }, }); // Assert expect(formattedUrl).to.equal('http://f00.bar/?f00=bar&bar=f00'); }); }); describe('validateCacheOptions', () => { it('does not accept null as argument', () => { expect(() => validateCacheOptions(null)).to.throw(TypeError); }); it('accepts an empty object', () => { expect(() => validateCacheOptions({})).not.to.throw( 'Property `useCache` must be a `boolean`', ); }); describe('the useCache property', () => { it('accepts a boolean', () => { expect(() => validateCacheOptions({ useCache: false })).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ useCache: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ useCache: '' })).to.throw( 'Property `useCache` must be a `boolean`', ); }); }); describe('the methods property', () => { it('accepts an array with the value `get`', () => { expect(() => validateCacheOptions({ methods: ['get'] })).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ methods: undefined })).not.to.throw; }); it('does not accept anything else', () => { expect(() => validateCacheOptions({ methods: [] })).to.throw( 'Cache can only be utilized with `GET` method', ); expect(() => validateCacheOptions({ methods: ['post'] })).to.throw( 'Cache can only be utilized with `GET` method', ); expect(() => validateCacheOptions({ methods: ['get', 'post'] })).to.throw( 'Cache can only be utilized with `GET` method', ); }); }); describe('the maxAge property', () => { it('accepts a finite number', () => { expect(() => validateCacheOptions({ maxAge: 42 })).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ maxAge: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ maxAge: 'string' })).to.throw( 'Property `maxAge` must be a finite `number`', ); expect(() => validateCacheOptions({ maxAge: Infinity })).to.throw( 'Property `maxAge` must be a finite `number`', ); }); }); describe('the invalidateUrls property', () => { it('accepts an array', () => { // @ts-ignore Typescript requires this to be an array of string, but this is not checked by validateCacheOptions expect(() => validateCacheOptions({ invalidateUrls: [6, 'elements', 'in', 1, true, Array] }), ).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ invalidateUrls: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ invalidateUrls: 'not-an-array' })).to.throw( 'Property `invalidateUrls` must be an `Array` or `undefined`', ); }); }); describe('the invalidateUrlsRegex property', () => { it('accepts a regular expression', () => { expect(() => validateCacheOptions({ invalidateUrlsRegex: /this is a very picky regex/ })) .not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ invalidateUrlsRegex: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ invalidateUrlsRegex: 'a string is not a regex' }), ).to.throw('Property `invalidateUrlsRegex` must be a `RegExp` or `undefined`'); }); }); describe('the requestIdFunction property', () => { it('accepts a function', () => { // @ts-ignore Typescript requires the requestIdFunction to return a string, but this is not checked by validateCacheOptions expect(() => validateCacheOptions({ requestIdFunction: () => ['this-is-ok-outside-typescript'] }), ).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ requestIdFunction: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ requestIdFunction: 'not a function' })).to.throw( 'Property `requestIdFunction` must be a `function`', ); }); }); describe('the contentTypes property', () => { it('accepts an array', () => { // @ts-ignore Typescript requires this to be an array of string, but this is not checked by validateCacheOptions expect(() => validateCacheOptions({ contentTypes: [6, 'elements', 'in', 1, true, Array] })) .not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ contentTypes: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ contentTypes: 'not-an-array' })).to.throw( 'Property `contentTypes` must be an `Array` or `undefined`', ); }); }); describe('the maxResponseSize property', () => { it('accepts a finite number', () => { expect(() => validateCacheOptions({ maxResponseSize: 42 })).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ maxResponseSize: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ maxResponseSize: 'string' })).to.throw( 'Property `maxResponseSize` must be a finite `number`', ); expect(() => validateCacheOptions({ maxResponseSize: Infinity })).to.throw( 'Property `maxResponseSize` must be a finite `number`', ); }); }); describe('the maxCacheSize property', () => { it('accepts a finite number', () => { expect(() => validateCacheOptions({ maxCacheSize: 42 })).not.to.throw; }); it('accepts undefined', () => { expect(() => validateCacheOptions({ maxCacheSize: undefined })).not.to.throw; }); it('does not accept anything else', () => { // @ts-ignore expect(() => validateCacheOptions({ maxCacheSize: 'string' })).to.throw( 'Property `maxCacheSize` must be a finite `number`', ); expect(() => validateCacheOptions({ maxCacheSize: Infinity })).to.throw( 'Property `maxCacheSize` must be a finite `number`', ); }); }); }); describe('invalidateMatchingCache', () => { beforeEach(() => { sinon.spy(ajaxCache, 'delete'); sinon.spy(ajaxCache, 'deleteMatching'); sinon.spy(pendingRequestStore, 'resolve'); sinon.spy(pendingRequestStore, 'resolveMatching'); }); afterEach(() => { sinon.restore(); }); it('calls delete on the ajaxCache and calls resolve on the pendingRequestStore', () => { // Arrange const requestId = 'request-id'; // Act invalidateMatchingCache(requestId, {}); // Assert expect(ajaxCache.delete).to.have.been.calledOnce; expect(pendingRequestStore.resolve.calledOnce).to.be.true; expect(ajaxCache.delete.calledWith(requestId)).to.be.true; expect(pendingRequestStore.resolve.calledWith(requestId)).to.be.true; }); it('calls invalidateMatching for all URL items in the invalidateUrls argument', () => { // Arrange const requestId = 'request-id'; const invalidateUrls = ['https://f00.bar/']; // Act invalidateMatchingCache(requestId, { invalidateUrls }); // Assert expect(ajaxCache.delete.calledTwice).to.be.true; expect(pendingRequestStore.resolve.calledTwice).to.be.true; expect(ajaxCache.delete.calledWith(requestId)).to.be.true; expect(pendingRequestStore.resolve.calledWith(requestId)).to.be.true; expect(ajaxCache.delete.calledWith('https://f00.bar/')).to.be.true; expect(pendingRequestStore.resolve.calledWith('https://f00.bar/')).to.be.true; }); it('calls invalidateMatching when the invalidateUrlsRegex argument is passed', () => { // Arrange const requestId = 'request-id'; const invalidateUrlsRegex = 'f00'; // Act invalidateMatchingCache(requestId, { invalidateUrlsRegex }); // Assert expect(ajaxCache.delete.calledOnce).to.be.true; expect(ajaxCache.deleteMatching.calledOnce).to.be.true; expect(pendingRequestStore.resolve.calledOnce).to.be.true; expect(pendingRequestStore.resolveMatching.calledOnce).to.be.true; expect(ajaxCache.delete.calledWith(requestId)).to.be.true; expect(pendingRequestStore.resolve.calledWith(requestId)).to.be.true; expect(ajaxCache.deleteMatching.calledWith('f00')).to.be.true; expect(pendingRequestStore.resolveMatching.calledWith('f00')).to.be.true; }); }); describe('isCurrentSessionId', () => { it('returns true for the current session id', () => { resetCacheSession('the-id'); expect(isCurrentSessionId('the-id')).to.equal(true); }); it('returns true for the current session id', () => { resetCacheSession('the-id'); expect(isCurrentSessionId('a-different-id')).to.equal(false); }); }); });