Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track unique history location states #14011

Merged
merged 1 commit into from
Jan 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ember-testing-resume-test": null,
"ember-factory-for": true,
"ember-no-double-extend": null,
"ember-routing-router-service": null
"ember-routing-router-service": null,
"ember-unique-location-history-state": null
}
}
27 changes: 26 additions & 1 deletion packages/ember-routing/lib/location/history_location.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
get,
set
set,
isFeatureEnabled
} from 'ember-metal';

import { Object as EmberObject } from 'ember-runtime';
Expand All @@ -13,6 +14,19 @@ import EmberLocation from './api';

let popstateFired = false;

let _uuid;

if (isFeatureEnabled('ember-unique-location-history-state')) {
_uuid = function _uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r, v;
r = Math.random() * 16 | 0;
v = c === 'x' ? r : r & 3 | 8;
return v.toString(16);
});
}
}

/**
Ember.HistoryLocation implements the location API using the browser's
history.pushState API.
Expand Down Expand Up @@ -134,6 +148,10 @@ export default EmberObject.extend({
from getState may be null if an iframe has changed a window's
history.

The object returned will contain a `path` for the given state as well
as a unique state `id`. The state index will allow the app to distinguish
between two states with similar paths but should be unique from one another.

@private
@method getState
@return state {Object}
Expand All @@ -155,6 +173,9 @@ export default EmberObject.extend({
*/
pushState(path) {
let state = { path };
if (isFeatureEnabled('ember-unique-location-history-state')) {
state.uuid = _uuid();
}

get(this, 'history').pushState(state, null, path);

Expand All @@ -173,6 +194,10 @@ export default EmberObject.extend({
*/
replaceState(path) {
let state = { path };
if (isFeatureEnabled('ember-unique-location-history-state')) {
state.uuid = _uuid();
}

get(this, 'history').replaceState(state, null, path);

this._historyState = state;
Expand Down
112 changes: 82 additions & 30 deletions packages/ember-routing/tests/location/history_location_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { set, run } from 'ember-metal';
import {
isFeatureEnabled,
set,
run
} from 'ember-metal';
import HistoryLocation from '../../location/history_location';

let FakeHistory, HistoryTestLocation, location;
Expand Down Expand Up @@ -110,48 +114,96 @@ QUnit.test('base URL is removed when retrieving the current pathname', function(
location.initState();
});

QUnit.test('base URL is preserved when moving around', function() {
expect(1);
if (isFeatureEnabled('ember-unique-location-history-state')) {
QUnit.test('base URL is preserved when moving around', function() {
expect(2);

HistoryTestLocation.reopen({
init() {
this._super(...arguments);
HistoryTestLocation.reopen({
init() {
this._super(...arguments);

set(this, 'location', mockBrowserLocation('/base/foo/bar'));
set(this, 'baseURL', '/base/');
}
set(this, 'location', mockBrowserLocation('/base/foo/bar'));
set(this, 'baseURL', '/base/');
}
});

createLocation();
location.initState();
location.setURL('/one/two');

equal(location._historyState.path, '/base/one/two');
ok(location._historyState.uuid);
});

createLocation();
location.initState();
location.setURL('/one/two');
QUnit.test('setURL continues to set even with a null state (iframes may set this)', function() {
expect(2);

equal(location._historyState.path, '/base/one/two');
});
createLocation();
location.initState();

QUnit.test('setURL continues to set even with a null state (iframes may set this)', function() {
expect(1);
FakeHistory.pushState(null);
location.setURL('/three/four');

createLocation();
location.initState();
equal(location._historyState.path, '/three/four');
ok(location._historyState.uuid);
});

FakeHistory.pushState(null);
location.setURL('/three/four');
QUnit.test('replaceURL continues to set even with a null state (iframes may set this)', function() {
expect(2);

equal(location._historyState.path, '/three/four');
});
createLocation();
location.initState();

QUnit.test('replaceURL continues to set even with a null state (iframes may set this)', function() {
expect(1);
FakeHistory.pushState(null);
location.replaceURL('/three/four');

createLocation();
location.initState();
equal(location._historyState.path, '/three/four');
ok(location._historyState.uuid);
});
} else {
QUnit.test('base URL is preserved when moving around', function() {
expect(1);

FakeHistory.pushState(null);
location.replaceURL('/three/four');
HistoryTestLocation.reopen({
init() {
this._super(...arguments);

equal(location._historyState.path, '/three/four');
});
set(this, 'location', mockBrowserLocation('/base/foo/bar'));
set(this, 'baseURL', '/base/');
}
});

createLocation();
location.initState();
location.setURL('/one/two');

equal(location._historyState.path, '/base/one/two');
});

QUnit.test('setURL continues to set even with a null state (iframes may set this)', function() {
expect(1);

createLocation();
location.initState();

FakeHistory.pushState(null);
location.setURL('/three/four');

equal(location._historyState.path, '/three/four');
});

QUnit.test('replaceURL continues to set even with a null state (iframes may set this)', function() {
expect(1);

createLocation();
location.initState();

FakeHistory.pushState(null);
location.replaceURL('/three/four');

equal(location._historyState.path, '/three/four');
});
}

QUnit.test('HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL', function() {
expect(1);
Expand Down