Jsdom: ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ก pushState/onpopstate/window.location์ด ์ž˜ ๊ด€๋ฆฌ๋˜์ง€ ์•Š์Œ

์— ๋งŒ๋“  2016๋…„ 07์›” 17์ผ  ยท  4์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: jsdom/jsdom

์•ˆ๋…•ํ•˜์„ธ์š”,

์—ญ์‚ฌ ๊ด€๋ฆฌ๊ฐ€ ์™„์ „ํ•˜์ง€ ์•Š๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.
๋‚ด ์ธก์˜ ์ด ํ…Œ์ŠคํŠธ๋Š” jsdom ๋ฒ„์ „ 9.4.1์—์„œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

    describe('jsdom history management', () => {
        before(() => {
        // Init DOM with a fake document
        // <base> and uri (initial uri) allow to do pushState in jsdom
            jsdom.env({
                html: `
                 <html>
                     <head>
                         <base href="http://localhost:8080/"></base>
                     </head>
                  </html>
                `,
                url: 'http://localhost:8080/',
                done(err, window) {
                    global.window = window;
                    global.document = window.document;
                    window.console = global.console;
                }
            });
        });

        it('location not updated', () => {
            window.history.pushState({}, 'route1', '/route1');
            assert(window.location.pathname === '/route1'); // this test is OK
            window.history.back();
            assert(window.location.pathname === '/'); // this test fails pathname still with value '/route1'
        });

        it('onpopstate not called', () => {
            window.onpopstate = () => { };
            const spy_onpopstate = sinon.spy(window, 'onpopstate');
            window.history.pushState({}, 'route1', '/route1');
            window.history.back();
            window.history.forward();
            assert(spy_onpopstate.called);// this test fails as well
        });
    });

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ด ๊ฒŒ์‹œ๋ฌผ์„ ์ฐพ๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘๋™์‹œํ‚ค๋ ค๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ Sinon spy ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  history.pushState ๋˜๋Š” history.replaceState ๋ฉ”์„œ๋“œ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  4 ๋Œ“๊ธ€

๋„ค. jsdom์€ ์™„์ „ํžˆ ์ƒˆ๋กœ์šด ์ฐฝ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•„์ง ์ผ๋ฐ˜์ ์œผ๋กœ ํƒ์ƒ‰์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•˜์ง€ ์•Š์€ ์–ด๋ ค์šด ์žฅ๊ธฐ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

์ด ๊ฒŒ์‹œ๋ฌผ์„ ์ฐพ๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘๋™์‹œํ‚ค๋ ค๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ Sinon spy ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  history.pushState ๋˜๋Š” history.replaceState ๋ฉ”์„œ๋“œ๋ฅผ ์กฐ๋กฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@crobinson42 ๋‚ด๊ฐ€ ๋‹น์‹ ์„ ์ดํ•ดํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ window.onpopstate์— ์กฐ๋กฑ๋„ ๋„์›€์ด ๋ ๊นŒ์š”?

๋‹ค์Œ์€ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค(TypeScript).

function firePopstateOnRoute(window: DOMWindow): void {
  const { history } = window;
  const originalBack = history.back;
  const originalForwards = history.forward;

  (history as unknown as {__proto__: History})['__proto__'].back = function patchedBack(this: History, ...args: Parameters<History['back']>): void {
    originalBack.apply(this, args);

    window.dispatchEvent(new PopStateEvent('popstate'));
  };

  (history as unknown as {__proto__: History}).__proto__.forward = function patchedForward(this: History, ...args: Parameters<History['forward']>): void {
    originalForwards.apply(this, args);

    window.dispatchEvent(new PopStateEvent('popstate'));
  };
}

export function mockBrowser(): void {
  const jsdom = new JSDOM('');
  const { window } = jsdom;

  firePopstateOnRoute(window);
}
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰