Skip to content

Commit

Permalink
fix(history-service): handle absolute server request urls (#561)
Browse files Browse the repository at this point in the history
  • Loading branch information
unstubbable authored Feb 12, 2020
1 parent ddbf8c2 commit be84e93
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 28 deletions.
99 changes: 72 additions & 27 deletions packages/history-service/src/__tests__/index.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ describe('defineHistoryService', () => {
});
});

it('retrieves consumer specific locations from the server request path', () => {
it('retrieves consumer specific locations from the server request url', () => {
destroyHistories();

const serverRequest: ServerRequestV1 = {
Expand Down Expand Up @@ -415,6 +415,27 @@ describe('defineHistoryService', () => {
HistoryServiceV2
>;

let historyBinding1: FeatureServiceBinding<HistoryServiceV2>;
let historyBinding2: FeatureServiceBinding<HistoryServiceV2>;
let historyService1: HistoryServiceV2;
let historyService2: HistoryServiceV2;
let history1: History;
let history2: History;

const createHistories = (serverRequest: ServerRequestV1 | undefined) => {
mockEnv.featureServices['s2:server-request'] = serverRequest;

const historyServiceBinder = createHistoryServiceBinder();

historyBinding1 = historyServiceBinder('test1');
historyService1 = historyBinding1.featureService;
history1 = historyService1.history;

historyBinding2 = historyServiceBinder('test2');
historyService2 = historyBinding2.featureService;
history2 = historyService2.history;
};

beforeEach(() => {
mockEnv = {featureServices: {'s2:logger': stubbedLogger}};

Expand All @@ -426,34 +447,11 @@ describe('defineHistoryService', () => {

return sharedHistoryService['2.0.0'];
};

createHistories({url: '/example', cookies: {}, headers: {}});
});

describe('#history', () => {
let historyBinding1: FeatureServiceBinding<HistoryServiceV2>;
let historyBinding2: FeatureServiceBinding<HistoryServiceV2>;
let historyService1: HistoryServiceV2;
let historyService2: HistoryServiceV2;
let history1: History;
let history2: History;

const createHistories = (serverRequest: ServerRequestV1 | undefined) => {
mockEnv.featureServices['s2:server-request'] = serverRequest;

const historyServiceBinder = createHistoryServiceBinder();

historyBinding1 = historyServiceBinder('test1');
historyService1 = historyBinding1.featureService;
history1 = historyService1.history;

historyBinding2 = historyServiceBinder('test2');
historyService2 = historyBinding2.featureService;
history2 = historyService2.history;
};

beforeEach(() =>
createHistories({url: '/example', cookies: {}, headers: {}})
);

describe('when no server request is provided', () => {
it('throws an error', () => {
expect(() => createHistories(undefined)).toThrowError(
Expand Down Expand Up @@ -509,7 +507,7 @@ describe('defineHistoryService', () => {
});
});

it('retrieves consumer specific locations from the server request path', () => {
it('retrieves consumer specific locations from a relative server request url', () => {
const serverRequest: ServerRequestV1 = {
url: createUrl({test1: '/foo', test2: 'bar'}, {relative: true}),
cookies: {},
Expand All @@ -522,6 +520,19 @@ describe('defineHistoryService', () => {
expect(history2.location).toMatchObject({pathname: '/bar'});
});

it('retrieves consumer specific locations from an absolute server request url', () => {
const serverRequest: ServerRequestV1 = {
url: createUrl({test1: '/foo', test2: 'bar'}, {relative: false}),
cookies: {},
headers: {}
};

createHistories(serverRequest);

expect(history1.location).toMatchObject({pathname: '/foo'});
expect(history2.location).toMatchObject({pathname: '/bar'});
});

describe('when getConsumerPathFromRootLocation returns undefined', () => {
it('returns the default location', () => {
expect(history1.location).toMatchObject({
Expand Down Expand Up @@ -725,6 +736,40 @@ describe('defineHistoryService', () => {
});
});

describe('#rootHistory', () => {
describe('#location', () => {
it('returns the root location containing all consumer locations for a server request with a relative url', () => {
const serverRequest: ServerRequestV1 = {
url: createUrl({test1: '/foo', test2: 'bar'}, {relative: true}),
cookies: {},
headers: {}
};

createHistories(serverRequest);

expect(historyService1.rootHistory.location).toMatchObject({
pathname: '/',
search: createSearch({test1: '/foo', test2: 'bar'})
});
});

it('returns the root location containing all consumer locations for a server request with an absolute url', () => {
const serverRequest: ServerRequestV1 = {
url: createUrl({test1: '/foo', test2: 'bar'}, {relative: false}),
cookies: {},
headers: {}
};

createHistories(serverRequest);

expect(historyService1.rootHistory.location).toMatchObject({
pathname: '/',
search: createSearch({test1: '/foo', test2: 'bar'})
});
});
});
});

describe('when no Logger Feature Service is provided', () => {
let consoleWarnSpy: jest.SpyInstance;

Expand Down
12 changes: 11 additions & 1 deletion packages/history-service/src/internal/static-root-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ import {
RootLocation,
RootLocationDescriptorObject
} from '../create-root-location-transformer';
import {URL} from './url';

function isAbsolute(url: string): boolean {
return /^https?:\/\//.test(url);
}

export class StaticRootHistory implements RootHistory {
public readonly length = 1;
public location: RootLocation;

public constructor(serverRequest: ServerRequestV1) {
this.location = history.createLocation(serverRequest.url);
if (isAbsolute(serverRequest.url)) {
const {pathname, search} = new URL(serverRequest.url);
this.location = history.createLocation({pathname, search});
} else {
this.location = history.createLocation(serverRequest.url);
}
}

/* istanbul ignore next */
Expand Down
8 changes: 8 additions & 0 deletions packages/history-service/src/internal/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* istanbul ignore next (not fully coverable on Node 10) */
const URLPonyfill: typeof URL =
typeof URL === 'undefined' && typeof window === 'undefined'
? // tslint:disable-next-line:no-eval https://stackoverflow.com/a/41063795/10385541
eval('require')('url').URL
: URL;

export {URLPonyfill as URL};

0 comments on commit be84e93

Please sign in to comment.