From 772bf2b81509b58518d9e3350722bf14143e26ed Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 5 Feb 2020 11:54:05 +0100 Subject: [PATCH] test: add tests for encoding --- __tests__/mockWarn.ts | 103 +++++++++++++++++++++++++++++++ __tests__/parseQuery.spec.ts | 29 +++++++++ __tests__/stringifyQuery.spec.ts | 22 +++++++ __tests__/url.spec.ts | 17 +++++ src/router.ts | 1 - 5 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 __tests__/mockWarn.ts create mode 100644 __tests__/parseQuery.spec.ts create mode 100644 __tests__/stringifyQuery.spec.ts diff --git a/__tests__/mockWarn.ts b/__tests__/mockWarn.ts new file mode 100644 index 00000000..e080d955 --- /dev/null +++ b/__tests__/mockWarn.ts @@ -0,0 +1,103 @@ +declare global { + namespace jest { + interface Matchers { + toHaveBeenWarned(): R + toHaveBeenWarnedLast(): R + toHaveBeenWarnedTimes(n: number): R + } + } +} + +export function mockWarn() { + expect.extend({ + toHaveBeenWarned(received: string) { + asserted.add(received) + const passed = warn.mock.calls.some( + args => args[0].indexOf(received) > -1 + ) + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned.`, + } + } else { + const msgs = warn.mock.calls.map(args => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedLast(received: string) { + asserted.add(received) + const passed = + warn.mock.calls[warn.mock.calls.length - 1][0].indexOf(received) > -1 + if (passed) { + return { + pass: true, + message: () => `expected "${received}" not to have been warned last.`, + } + } else { + const msgs = warn.mock.calls.map(args => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been warned last.\n\nActual messages:\n\n - ${msgs}`, + } + } + }, + + toHaveBeenWarnedTimes(received: string, n: number) { + asserted.add(received) + let found = 0 + warn.mock.calls.forEach(args => { + if (args[0].indexOf(received) > -1) { + found++ + } + }) + + if (found === n) { + return { + pass: true, + message: () => + `expected "${received}" to have been warned ${n} times.`, + } + } else { + return { + pass: false, + message: () => + `expected "${received}" to have been warned ${n} times but got ${found}.`, + } + } + }, + }) + + let warn: jest.SpyInstance + const asserted: Set = new Set() + + beforeEach(() => { + asserted.clear() + warn = jest.spyOn(console, 'warn') + warn.mockImplementation(() => {}) + }) + + afterEach(() => { + const assertedArray = Array.from(asserted) + const nonAssertedWarnings = warn.mock.calls + .map(args => args[0]) + .filter(received => { + return !assertedArray.some(assertedMsg => { + return received.indexOf(assertedMsg) > -1 + }) + }) + warn.mockRestore() + if (nonAssertedWarnings.length) { + nonAssertedWarnings.forEach(warning => { + console.warn(warning) + }) + throw new Error(`test case threw unexpected warnings.`) + } + }) +} diff --git a/__tests__/parseQuery.spec.ts b/__tests__/parseQuery.spec.ts new file mode 100644 index 00000000..fd7988ad --- /dev/null +++ b/__tests__/parseQuery.spec.ts @@ -0,0 +1,29 @@ +import { parseQuery } from '../src/history/common' +import { mockWarn } from './mockWarn' + +describe('parseQuery', () => { + mockWarn() + it('decodes values in query', () => { + expect(parseQuery('e=%25')).toEqual({ + e: '%', + }) + }) + + it('decodes array values in query', () => { + expect(parseQuery('e=%25&e=%22')).toEqual({ + e: ['%', '"'], + }) + expect(parseQuery('e=%25&e=a')).toEqual({ + e: ['%', 'a'], + }) + }) + + // this is for browsers like IE that allow invalid characters + it('keep invalid values as is', () => { + expect(parseQuery('e=%&e=%25')).toEqual({ + e: ['%', '%'], + }) + + expect('decoding "%"').toHaveBeenWarnedTimes(1) + }) +}) diff --git a/__tests__/stringifyQuery.spec.ts b/__tests__/stringifyQuery.spec.ts new file mode 100644 index 00000000..8ea5a46a --- /dev/null +++ b/__tests__/stringifyQuery.spec.ts @@ -0,0 +1,22 @@ +import { stringifyQuery } from '../src/history/common' +import { mockWarn } from './mockWarn' + +describe('stringifyQuery', () => { + mockWarn() + + it('stringifies multiple values', () => { + expect(stringifyQuery({ e: 'a', b: 'c' })).toEqual('e=a&b=c') + }) + + it('stringifies arrays', () => { + expect(stringifyQuery({ e: ['b', 'a'] })).toEqual('e=b&e=a') + }) + + it('encodes values', () => { + expect(stringifyQuery({ e: '%', b: 'c' })).toEqual('e=%25&b=c') + }) + + it('encodes values in arrays', () => { + expect(stringifyQuery({ e: ['%', 'a'], b: 'c' })).toEqual('e=%25&e=a&b=c') + }) +}) diff --git a/__tests__/url.spec.ts b/__tests__/url.spec.ts index 3853b51d..ea4fe7b1 100644 --- a/__tests__/url.spec.ts +++ b/__tests__/url.spec.ts @@ -56,6 +56,13 @@ describe('parseURL', () => { query: { a: ['one', 'two', 'three'] }, }) }) + + it('calls parseQuery', () => { + const parseQuery = jest.fn() + originalParseURL(parseQuery, '/?é=é&é=a') + expect(parseQuery).toHaveBeenCalledTimes(1) + expect(parseQuery).toHaveBeenCalledWith('é=é&é=a') + }) }) describe('stringifyURL', () => { @@ -114,6 +121,16 @@ describe('stringifyURL', () => { }) ).toBe('/path?foo=a&bar=b#hey') }) + + it('calls stringifyQuery', () => { + const stringifyQuery = jest.fn() + originalStringifyURL(stringifyQuery, { + path: '/', + query: { é: 'é', b: 'a' }, + }) + expect(stringifyQuery).toHaveBeenCalledTimes(1) + expect(stringifyQuery).toHaveBeenCalledWith({ é: 'é', b: 'a' }) + }) }) describe('normalizeLocation', () => { diff --git a/src/router.ts b/src/router.ts index 258938c5..7fd6b43c 100644 --- a/src/router.ts +++ b/src/router.ts @@ -168,7 +168,6 @@ export function createRouter({ // const objectLocation = routerLocationAsObject(location) if (typeof location === 'string') { // TODO: remove as cast when redirect is removed from matcher - // TODO: ensure parseURL encodes the query in fullPath but not in query object let locationNormalized = parseURL(parseQuery, location) let matchedRoute = matcher.resolve( { path: locationNormalized.path }, -- 2.47.3