Skip to content

Commit

Permalink
fix: now more spec compliant on exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
wheresrhys committed Jul 30, 2024
1 parent 0b40de7 commit ceec07f
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 52 deletions.
8 changes: 5 additions & 3 deletions packages/core/src/RequestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ export function getQuery(url) {

/**
*
* @param {Headers | [string, string][] | Record < string, string > | Object.<string, string | number> | HeadersInit} headers
* @param {HeadersInit | Object.<string, string |number>} headers
* @returns {Object.<string, string>}
*/
export const normalizeHeaders = (headers) => {
let entries = headers;
let entries;
if (headers instanceof Headers) {
entries = [...headers.entries()];
} else if (!Array.isArray(headers)) {
} else if (Array.isArray(headers)) {
entries = headers;
} else {
entries = Object.entries(headers);
}
return Object.fromEntries(
Expand Down
32 changes: 13 additions & 19 deletions packages/core/src/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,33 +93,27 @@ function shouldSendAsObject(responseInput) {
return true;
}

function throwSpecExceptions(callLog) {
const headers = callLog.options.headers;
/**
*
* @param {CallLog} callLog
*/
function throwSpecExceptions({ url, options: { headers, method, body } }) {
if (headers) {
Object.entries(headers).forEach(([key]) => {
if (/\s/.test(key)) {
throw new TypeError('no way - space in header');
throw new TypeError('Invalid name');
}

// if (/\s/.test(value)) {
// throw new TypeError('no way - space in header');
// }
});
}
if (/^[a-z]+\:\/\/[^:]+:[^@]+@/.test(callLog.url)) {
throw new TypeError('no way - contains credentials');
}

if (['navigate', 'websocket'].includes(callLog.options.mode)) {
throw new TypeError('no way - wrong mode');
const urlObject = new URL(url);
if (urlObject.username || urlObject.password) {
throw new TypeError(
`Request cannot be constructed from a URL that includes credentials: ${url}`,
);
}

console.log(callLog.options);
if (
['get', 'head'].includes(callLog.options.method) &&
callLog.options.body
) {
throw new TypeError('no way - wrong method for body');
if (['get', 'head'].includes(method) && body) {
throw new TypeError('Request with GET/HEAD method cannot have body.');
}
}

Expand Down
17 changes: 9 additions & 8 deletions packages/core/src/__tests__/CallHistory.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('CallHistory', () => {
});

const fetchTheseUrls = (...urls) =>
Promise.all(urls.map(fm.fetchHandler.bind(fm)));
Promise.all(urls.map((url) => fm.fetchHandler(url)));

describe('helper methods', () => {
describe('called()', () => {
Expand Down Expand Up @@ -208,8 +208,8 @@ describe('CallHistory', () => {
describe('boolean and named route filters', () => {
it('can retrieve calls matched by non-fallback routes', async () => {
fm.route('http://a.com/', 200).catch();

await fetchTheseUrls('http://a.com/', 'http://b.com/');

expectSingleUrl(true)('http://a.com/');
expectSingleUrl('matched')('http://a.com/');
});
Expand Down Expand Up @@ -298,12 +298,13 @@ describe('CallHistory', () => {
headers: { a: 'z' },
});
expect(filteredCalls.length).toEqual(1);

expect(filteredCalls[0]).toMatchObject(
expect.objectContaining({
url: 'http://a.com/',
options: {
options: expect.objectContaining({
headers: { a: 'z' },
},
}),
}),
);
});
Expand All @@ -326,9 +327,9 @@ describe('CallHistory', () => {
expect(filteredCalls[0]).toMatchObject(
expect.objectContaining({
url: 'http://b.com/',
options: {
options: expect.objectContaining({
headers: { a: 'z' },
},
}),
}),
);
});
Expand All @@ -347,9 +348,9 @@ describe('CallHistory', () => {
expect(filteredCalls[0]).toMatchObject(
expect.objectContaining({
url: 'http://a.com/',
options: {
options: expect.objectContaining({
headers: { a: 'z' },
},
}),
}),
);
});
Expand Down
35 changes: 15 additions & 20 deletions packages/core/src/__tests__/spec-compliance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,33 @@ describe('Spec compliance', () => {
'has space': 'ok',
},
}),
).rejects.toThrow(new TypeError('asdasdsa'));
});
it('reject on invalid header value', async () => {
await expect(
fetchMock.fetchHandler('http://a.com', {
headers: [['a', 'b', 'c']],
}),
).rejects.toThrow(new TypeError('asdasdsa'));
).rejects.toThrow(new TypeError('Invalid name'));
});
it('reject on url containing credentials', async () => {
await expect(
fetchMock.fetchHandler('http://user:password@a.com'),
).rejects.toThrow(new TypeError('asdasdsa'));
});
it('reject on invalid modes', async () => {
await expect(
fetchMock.fetchHandler('http://a.com', { mode: 'websocket' }),
).rejects.toThrow(new TypeError('asdasdsa'));
await expect(
fetchMock.fetchHandler('http://a.com', { mode: 'navigate' }),
).rejects.toThrow(new TypeError('asdasdsa'));
).rejects.toThrow(
new TypeError(
'Request cannot be constructed from a URL that includes credentials: http://user:password@a.com/',
),
);
});
it('reject if the request method is GET or HEAD and the body is non-null.', async () => {
await expect(
fetchMock.fetchHandler('http://a.com', { body: 'a' }),
).rejects.toThrow(new TypeError('asdasdsa'));
).rejects.toThrow(
new TypeError('Request with GET/HEAD method cannot have body.'),
);
await expect(
fetchMock.fetchHandler('http://a.com', { body: 'a', method: 'GET' }),
).rejects.toThrow(new TypeError('asdasdsa'));
).rejects.toThrow(
new TypeError('Request with GET/HEAD method cannot have body.'),
);
await expect(
fetchMock.fetchHandler('http://a.com', { body: 'a', method: 'HEAD' }),
).rejects.toThrow(new TypeError('asdasdsa'));
).rejects.toThrow(
new TypeError('Request with GET/HEAD method cannot have body.'),
);
});
});
});
4 changes: 2 additions & 2 deletions packages/core/types/RequestUtils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ export function createCallLogFromUrlAndOptions(url: string | object, options: Re
export function createCallLogFromRequest(request: Request, options: RequestInit): Promise<CallLog>;
export function getPath(url: string): string;
export function getQuery(url: string): string;
export function normalizeHeaders(headers: Headers | [string, string][] | Record<string, string> | {
export function normalizeHeaders(headers: HeadersInit | {
[x: string]: string | number;
} | HeadersInit): {
}): {
[x: string]: string;
};
export type DerivedRequestOptions = {
Expand Down

0 comments on commit ceec07f

Please sign in to comment.