-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
window.location.href can't be changed in tests. #890
Comments
You are right, this is indeed a jsdom issue. At Facebook, what we have done to work around this, is use this: Object.defineProperty(window.location, 'href', {
writable: true,
value: 'some url'
}); this works for us, however we are still on jsdom 7 internally. I'll close this, as I believe the |
Cool, thanks I will try this out. |
@cpojer, I can't seem to figure out what I need to click on to reopen this issue... Is there anyway in |
old ticket but for those still having this issue we've started using
|
Thanks @th3fallen . That's cool! |
Btw @cpojer I start at FB on 1May.... ;P |
Nice! |
I'm trying to migrate our tests from Mocha+Chai+Sinon.js to Jest and can't figure out how to change location for a particular test. |
@okovpashko we're planning to expose jsdom to the environment: #2460 |
|
@thymikee I saw that issue but thought that the proposition was rejected. I need to change not only the href, so I wrote simple method, that may be useful for someone who will read this thread: const setURL = (url) => {
const parser = document.createElement('a');
parser.href = url;
['href', 'protocol', 'host', 'hostname', 'origin', 'port', 'pathname', 'search', 'hash'].forEach(prop => {
Object.defineProperty(window.location, prop, {
value: parser[prop],
writable: true,
});
});
}; |
Apologies for dragging out this thread further, but I have tried mocking out push function as suggested...
but I'm still getting a jsdom error that I can't seem to get round:
I realise this is a jsdom error, but for those who have solved this is there any more setup context you could share that might let me get round this? Thanks |
@matt-dalton try my suggestion in #890 (comment) works well for me |
@matt-dalton what's your URL? do you have testURL set in your |
@ianlyons Yeah I set value of @th3fallen If I understand you correctly, I don't think this works for my use case. Are you passing the url as a context value that causes assign to be triggered? I am trying to put together a rudimentary integration test, so I want to check how the router responds to the initial data load. I have mocked the API response, and then need the URL change to take place using the app logic (i.e. I do not want to trigger it externally myself). |
|
@msholty-fd you could try this approach: const origLocation = document.location.href;
let location = origLocation;
beforeAll(() => {
const parser = document.createElement('a');
['href', 'protocol', 'host', 'hostname', 'origin', 'port', 'pathname', 'search', 'hash'].forEach(prop => {
Object.defineProperty(window.location, prop, {
get: function() {
parser.href = location;
return parser[prop];
}
});
});
});
afterEach(() => {
location = origLocation;
});
test('location 1', () => {
location = "https://www.google.com/";
console.log(document.location.href); // https://www.google.com/
});
test('location 2', () => {
console.log(document.location.href); // about:blank
}); |
It stopped working in Jest 22.0.1 Object.defineProperty(window.location, 'href', {
writable: true,
value: 'some url'
}); Error message: TypeError: Cannot redefine property: href
at Function.defineProperty (<anonymous>) |
Hmm, we might need to somehow allow people to call |
Opened a new issue related to this, since this one was closed: #5124 |
@SimenB I'm not convinced that Jest should fix this. JSDOM should allow |
I got I tried to assign a base url to the |
If you just want some other url than |
thanks @SimenB for your reply. No I was talking about TypeError: Could not parse "/login" as a URL I checked the source code of with global.jsdom = new JSDOM('<html><head> <base href="base_url" /></head></html>') but because I then found a solution which is to substitute |
@bochen2014 this issue has more information on how to use the newer version of jsdom: #5124 tl;dr: you can mock |
thanks @simon360 that's what I did ;-) just for people who may/will run into the same issue, to set the url for your jsdom // jest.config.js
module.exorts={
testURL: 'http://localhost:3000',
// or :
testEnvironmentOptions: {
url: "http://localhost:3000/",
referrer: "https://example.com/",
}
} note that this will set url for all your tests; |
Posted it on other ticket, but I'll post it here: Found nice solution for Jest 21.2.1 Ok, so far the easiest solution around this is:
Now you will have access to window object and then you can set URL to whatever you like during tests. it('Should set href url to testURL', () => {
// Here I set href to my needs, opinionated stuff bellow
const newUrl = 'http://localhost/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
Object.defineProperty(window.location, 'href', {
writable: true,
value: newUrl
});
console.log(window.location.href);
});
it('Should set pathname url to testURL', () => {
// Here I set href to my needs, opinionated stuff bellow
const newUrl = '/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
Object.defineProperty(window.location, 'pathname', {
writable: true,
value: newUrl
});
console.log(window.location.pathname);
}); Hopefully this helps someone. |
In case you're getting this error when using Vue, just use |
This works:
|
Or better yet...
|
Yeah, I have some functions deal with When I turn off
And now I have no idea about how to test my funtions. Anyone have any way to change the test |
And In my situation, have a field |
Excellent solution!!! Thank you very much! @Mike-Tran |
To get this working as of June 2019 I had to do this:
|
I use this.... window.history.pushState({}, '', `${url}/`); |
Probably part of my JSDOMTestWrapper can help somebody /** @type {Window} */
this.testWindowObject = Object.create(window);
const wnd = this.testWindowObject;
this.testWindowObject.history = {
state: null,
prev: { /** @todo immutable stack with the go(step) method emulation */
state: null,
pathname: null,
search: null,
},
go(step) {
logger.special('history go called', step);
logger.warn('history go has not supported yet');
},
back() {
this.state = this.prev.state;
wnd.location.pathname = this.prev.pathname;
wnd.location.search = this.prev.search;
const eventData = this.state ? { url: this.state.displayURL, newState: this.state, type: 'push' } : null;
wnd.sm.eventsService.triggerEvent(ROUTER_EVENTS.ROUTE_PUSH, eventData);
wnd.sm.urlService.simpleRouteTo(`${ wnd.location.pathname || '' }${ wnd.location.search || '' }`);
logger.special('history back emulated');
},
pushState(state, title, url) {
this.prev.state = Object.assign({}, this.state);
this.prev.pathname = '' + wnd.location.pathname;
this.prev.search = '' + wnd.location.search;
this.state = state;
if (title) wnd.document.title = title;
const [p, s] = url.split('?');
wnd.location.pathname = p;
wnd.location.search = s ? `?${ s }` : '';
logger.special('push state emulated', { state, title, url });
},
replaceState(state, title, url) {
this.prev.state = Object.assign({}, this.state);
this.prev.pathname = '' + wnd.location.pathname;
this.prev.search = '' + wnd.location.search;
this.state = state;
if (title) wnd.document.title = title;
const [p, s] = url.split('?');
wnd.location.pathname = p;
wnd.location.search = s ? `?${ s }` : '';
logger.special('replace state emulated', { state, title, url });
logger.special('test: urlService.getPathName()', wnd.sm.urlService.getPathName());
},
};
this.testWindowObject.innerWidth = WND_WIDTH;
this.testWindowObject.innerHeight = WND_HEIGHT;
this.testWindowObject.fetch = fetchFn;
this.testWindowObject.localStorage = lstMock;
this.testWindowObject.scrollTo = (x, y) => {
/** not implemented yet https://github.com/jsdom/jsdom/issues/1422 */
if (typeof x !== 'number' && (x.left || x.top)) {
y = x.top;
x = x.left;
}
// logger.info(`window.scrollTo(${ x }, ${ y })`);
};
if (fetchFn === JSDOMTestWrapper.FETCH_FN.DEV_MOCK) {
global.Request = RequestMock;
this.testWindowObject.Request = RequestMock;
}
if (href) {
this.testWindowObject.location = Object.assign({}, this.testWindowObject.location, urlapi.parse(href));
}
else {
this.testWindowObject.location = Object.assign({}, this.testWindowObject.location);
}
(function(ELEMENT) {
ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector;
ELEMENT.closest = ELEMENT.closest || function closest(selector) {
if (!this) return null;
if (this.matches(selector)) return this;
if (!this.parentElement) {return null}
else return this.parentElement.closest(selector)
};
ELEMENT.getBoundingClientRect = ELEMENT.getBoundingClientRect || (() =>
({ bottom: WND_HEIGHT, height: WND_HEIGHT, left: 0, right: WND_WIDTH, top: 0, width: WND_WIDTH, x: 0, y: 0 }));
}(Element.prototype));
this.testWindowObject.getBoundingClientRect = () =>
({ bottom: WND_HEIGHT, height: WND_HEIGHT, left: 0, right: WND_WIDTH, top: 0, width: WND_WIDTH, x: 0, y: 0 });
this.testWindowObject.__resizeListeners__ = [];
this.testWindowObject.__resizeTriggers__ = {};
this.testWindowObject._detectElementResize = {
removeResizeListener: () => {},
};
this.testWindowObject.matchMedia = jest.fn().mockImplementation(query => {
return {
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
};
});
this.rftpr = () => {};
this.mode = mode;
this.renderFirstTimePromise = new Promise((resolve) => {
this.rftpr = resolve;
});
this.marpr = () => {};
this.mobileAppReadyPromise = new Promise((resolve) => {
this.marpr = resolve;
});
if (mode === JSDOMTestWrapper.MODE.MOBILE_APP) {
this.testWindowObject.navigator = Object.assign({}, this.testWindowObject.navigator, {
language: storeKey,
appVersion: '5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Mobile Safari/537.36',
vendor: 'Google Inc.',
});
global.intercom = {
registerUnidentifiedUser: jest.fn(),
registerForPush: jest.fn(),
};
}
const XApp = mode ? MobileApp : App;
const app = <XApp window={ this.testWindowObject } rftResolve={ this.rftpr } storeKey={ storeKey } apiHost={ apiVersion } forceMobileDetection={ mode } />;
render(app, this.testWindowObject.document.body);
if (mode === JSDOMTestWrapper.MODE.MOBILE_APP) {
setTimeout(() => {
this.testWindowObject.sm.deviceService.appRestorePathHasInit = this.marpr;
this.testWindowObject.sm.deviceService.fireEvent(this.testWindowObject.document, 'deviceready');
}, 200);
} |
This approach works as of Sep 27, 2019: https://stackoverflow.com/a/54034379/1344144
|
Another solution works for me currently without writing
In your test file:
Learned from: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking. Thansk to Ryan! |
not working for me: TypeError: Assignment to read-only properties not allowed in strict mode it("should save hash when history is not found", () => {
const historyBkp = global.window.history;
delete global.window.history;
global.window.history = false;
externalLoader.savePageURL(urlTraining);
expect(window.location.hash).to.be.equal(`#page=${urlTraining}`);
global.window.history = historyBkp;
window.location.hash = "";
}); |
add this to the global file. delete global.window.location; |
I'm trying something similar, with location.assign, but seems this isn't working anymore. |
this works for me on jest 24.9.0 window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007'); |
I had to make code async in order to get this to work because I was running code inside a promise. so is working now 😃 |
How to test chenge location in vuex action ?
I have error:
|
what's the global? where's the global definition? |
this creates a Location with all the original functionality, but it's mockable: beforeAll(() => {
const location = window.location
delete global.window.location
global.window.location = Object.assign({}, location)
}) |
I had a really hard time getting this to work. I had a list of pathnames that needed to be checked against the current user's pathname describe('<GearMenuButton />', () => {
const defaultProps = {
label: 'Nav Menu Button'
}
it.only('should have Account username underlined if the URL pathname is in the list of URLs', () => {
const props = { locationUrls: ['/home.aspx', '/another.path'], ...defaultProps}
global.window = Object.create(window)
const pathname = '/home.aspx'
Object.defineProperty(window, 'location', {
value: {
pathname: pathname
},
writable: true
})
render(<GearMenuButton {...props} />)
const button = document.querySelector('.class-of-interest--active')
expect(button).toBeInTheDocument()
})
} |
In TypeScript 4.1.3, this (as well as all other suggestions to
|
I am facing the same issue and after trying the above solution ,getting 'cannot redefine window.href' |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Hi @cpojer,
This is actually more of a
jsdom@8
issue...see jsdom/jsdom#1388, but I want to pin here as well so Jest picks up whatever solution jsdom comes up with.Previously with
[email protected]/[email protected]
you could write a test like this:And that test would pass. Since
jsdom@8
this is no longer possible and these tests fail.Seems like jsdom is looking at some type of capability, just wanted to make sure that Jest will pick up that capability when it is available.
The text was updated successfully, but these errors were encountered: