Jest: Object.defineProperty๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ window.location์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2017๋…„ 12์›” 19์ผ  ยท  78์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/jest

_๊ธฐ๋Šฅ_์„ ์š”์ฒญํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด _๋ฒ„๊ทธ_๋ฅผ ๋ณด๊ณ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋ฒ„๊ทธ ์‹ ๊ณ 

ํ˜„์žฌ ํ–‰๋™์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ๋‚ด๋ถ€์—์„œ ๋‹ค์Œ์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

Object.defineProperty(location, "hostname", {
  value: "example.com",
  writable: true
});

๋‹ค์Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

    TypeError: Cannot redefine property: hostname
        at Function.defineProperty (<anonymous>)

์˜ˆ์ƒ๋˜๋Š” ๋™์ž‘์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

์ฝ”๋“œ๋Š” ์˜ˆ์™ธ๋ฅผ throwํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ window.location.hostname === "example.com" ๋Š” true๋กœ ํ‰๊ฐ€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ณด๊ธฐ์— jsdom ์ด์ œ window.location์„ ์œ„์กฐํ•  ์ˆ˜ ์—†๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค . window.location ๋‚ด์—์„œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ reconfigure ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ (#2460์— ๋”ฐ๋ผ) Jest๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด jsdom ๋ฅผ ๋…ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ •ํ™•ํ•œ Jest ๊ตฌ์„ฑ์„ ์ œ๊ณตํ•˜๊ณ  Jest, ๋…ธ๋“œ,yarn/npm ๋ฒ„์ „ ๋ฐ ์šด์˜ ์ฒด์ œ.

๋†๋‹ด ๋ฒ„์ „ : 22.0.1
๋…ธ๋“œ ๋ฒ„์ „ : 8.6.0
์›์‚ฌ ๋ฒ„์ „ : 1.2.0
OS : macOS ํ•˜์ด ์‹œ์—๋ผ 10.13.2

Discussion Wontfix

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

๋‚˜๋Š”๋ผ๋Š” NPM์— ์ƒˆ ํŒจํ‚ค์ง€๋ฅผ ๋ฐœํ‘œํ–ˆ๋‹ค jest-environment-jsdom-global ์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ ํ•จ๊ป˜ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์— ๋„์›€์ด ๋  ์ˆ˜์žˆ๋Š”, Object.defineProperty .

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

๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์‹ ๋งŒ์˜ JSDOMEnvironment๋ฅผ ๋งŒ๋“ค๊ณ  jsdom ๊ฐ์ฒด๋ฅผ ์ด์™€ ๊ฐ™์ด ์ „์—ญ์— ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const JSDOMEnvironment = require('jest-environment-jsdom');

module.exports = class CustomizedJSDomEnvironment extends JSDOMEnvironment {
  constructor(config) {
    super(config);
    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;
    return super.teardown();
  }
};

๊ทธ๋Ÿฐ ๋‹ค์Œ ์›ํ•˜๋Š” ๋Œ€๋กœ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ jsdom.reconfigure๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ข‹์€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ณต์œ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์•ฝ์†์ด๋ฏ€๋กœ super.teardown(); ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. btw

์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค. @oliverzy - ์‹œ๋„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”!

์ด๋ฅผ ๋ฌธ์„œํ™”ํ•  ์ ์ ˆํ•œ ์žฅ์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๊ฝค ์ž์ฃผ ๋‚˜์˜ค๋Š” ์งˆ๋ฌธ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฐ”๋ผ๊ฑด๋Œ€, ์ด๊ฒƒ์ด ๋ฌธ์„œ์— ํ†ตํ•ฉ๋œ๋‹ค๋ฉด ๋ฏธ๋ž˜์˜ ๋ฌธ์ œ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด ์†”๋ฃจ์…˜์€ ๋งค์šฐ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜๋‹ค.

ํ…Œ์ŠคํŠธ ํŒŒ์ผ ๋‚ด์—์„œ global ๊ฐ€ JSDom์˜ window ๊ฐœ์ฒด๋กœ ์„ค์ •๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฆ‰, ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ๋‚ด์—์„œ global ๋Š” window ์™€ ๋™์ผํ•˜์ง€๋งŒ JSDOMEnvironment ๋ฅผ ํ™•์žฅํ•˜๋Š” ํด๋ž˜์Šค ๋‚ด์—์„œ global ๋Š” Node ํ™˜๊ฒฝ์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

describe("test suite", () => {
  it("should not fail", () => {
    global.jsdom.reconfigure({
      url: "https://www.example.com/"
    });
  });
});

global.jsdom ์ด ์ •์˜๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ํ•จ์œผ๋กœ์จ ๊ทธ๊ฒƒ์„ ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋ณ„๋กœ ์†Œ๋ž€์Šค๋Ÿฝ์ง€๋Š” ์•Š๋‹ค.

const JSDOMEnvironment = require("jest-environment-jsdom");

module.exports = class JSDOMEnvironmentGlobal extends JSDOMEnvironment {
  constructor(config) {
    super(config);

    this.dom.window.jsdom = this.dom;
  }
};

์ด ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ ๋‚ด์˜ global.jsdom ๋Š” this.dom ์™€ ๊ฐ™๊ณ  ์œ„์˜ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๊ฐ€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜์—๊ฒŒ jsdom ๋ฅผ ์ž์ฒด window ๊ฐ์ฒด์˜ ์†์„ฑ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ ๊ฒฐ๊ตญ ๋ฌด๋„ˆ์งˆ ์ˆ˜๋ฐ–์— ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. ๋” ๊นจ๋—ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

ํ…Œ์ŠคํŠธ์—์„œ global.jsdom ๊ฐ€ ์•„๋‹Œ jsdom ๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@oliverzy ์ด๋ ‡๊ฒŒ?

describe("test suite", () => {
  it("should not fail", () => {
    jsdom.reconfigure({
      url: "https://www.example.com/"
    });
  });
});

๊ทธ๋Ÿฌ๋ฉด jsdom is not defined ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋งŒ ์ œ๊ฐ€ ์ž˜๋ชป ํ•ด์„ํ•˜๊ณ  ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@ simon360 ๊ตฌ์„ฑํ•˜์‹ญ์‹œ์˜ค testEnvironment @oliverzy์˜ ์ฝ”๋“œ๋ฅผ ์ฐธ์กฐ https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -String

@danielbayerlein ๋‚ด Jest ๊ตฌ์„ฑ์—๋Š” ๋‹ค์Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

"testEnvironment": "@wel-ui/jest-environment-jsdom-global"

์—ฌ๊ธฐ์„œ @wel-ui/jest-environment-jsdom-global ๋Š” ๋ชจ๋…ธ๋ ˆํฌ์— ์žˆ๋Š” ํŒจํ‚ค์ง€์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ jsdom ์— window jsdom ๋ฅผ ์„ค์ •ํ•˜๋Š” ์†”๋ฃจ์…˜์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ™˜๊ฒฝ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

BTW, ์›๋ž˜ ์†”๋ฃจ์…˜์ด ์ƒˆ ๋ฒ„์ „์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์•„๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?
์ด ํ•˜๋‚˜:

Object.defineProperty(location, "hostname", {
  value: "example.com",
  writable: true
});

@modestfake ์šฐ๋ฆฌ๋Š” JSDOM@9 ์—์„œ JSDOM@11 ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ–ˆ์Šต๋‹ˆ๋‹ค. ์ œ ์ถ”์ธก์œผ๋กœ๋Š” ๋ณ€์ˆ˜ ์ •์˜ ๋ฐฉ๋ฒ•์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@SimenB ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. jsdom reconfigure ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•œ ์„ค๋ช… ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค.

์ฐฝ์˜ ๋งจ ์œ„ ์†์„ฑ์€ ์‚ฌ์–‘์—์„œ [์œ„์กฐ ๋ถˆ๊ฐ€๋Šฅ]์œผ๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๊ตฌ์„ฑํ•  ์ˆ˜ ์—†๋Š” ์ž์ฒด ์†์„ฑ์ด๋ฏ€๋กœ Object.defineProperty๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ jsdom ๋‚ด๋ถ€์—์„œ ์‹คํ–‰๋˜๋Š” ์ผ๋ฐ˜ ์ฝ”๋“œ๋กœ ๋ฎ์–ด์“ฐ๊ฑฐ๋‚˜ ์ˆจ๊ธธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด ๋™์ž‘์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ƒˆ ์ €์žฅ์†Œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋กœ์ปฌ์—์„œ ๋ณต์ œํ•˜์—ฌ ๋ณต์ œํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

https://github.com/simon360/test-environment-for-jest

@simon360 ์žฌํ˜„
image

@ simon360 ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. global.jsdom ์ •์˜ํ•  ๋•Œ this ํ‚ค์›Œ๋“œ๋ฅผ ๋†“์ณค์Šต๋‹ˆ๋‹ค.

const JSDOMEnvironment = require("jest-environment-jsdom");

module.exports = class JSDOMEnvironmentGlobal extends JSDOMEnvironment {
  constructor(config) {
    super(config);

    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;

    return super.teardown();
  }
};

location.search ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha ์ด๊ฑด ์–ด๋•Œ?

jsdom.reconfigure({
  url: 'https://www.example.com/endpoint?queryparam1=15&queryparam2=test'
});

@modestfake ๊ฐ์‚ฌ

์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์ œ Jest ํ™˜๊ฒฝ ๊ฐœ์ฒด์˜ this.global ๊ฐ€ Jest ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—์„œ global ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ดํ•ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋„์™€์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๊ด€์‹ฌ์ด ์ถฉ๋ถ„ํ•˜๋‹ค๋ฉด ํ•ด๋‹น ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๋ณต๊ตฌ๋œ ๋ฒ„์ „์„ ํŒจํ‚ค์ง•ํ•˜๊ณ  npm ๋กœ jest-environment-jsdom-global npm ์— ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์•ž์œผ๋กœ Jest์—์„œ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋” ๊นจ๋—ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ window.location ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋งˆ์ฐฐ์ด ์ ์€ ๋ฐฉ๋ฒ•์ด ์•„๋‹™๋‹ˆ๋‹ค.

@jest-environment ์™€ ๊ฐ™์€ ์ƒˆ docblock ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด...

/**
 * @jest-url https://www.example.com/
 */

๋˜๋Š” JSDom์ด jest ๊ฐœ์ฒด์˜ ํŠน์ • ๋ถ€๋ถ„์— ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

jest.environment.jsdom.reconfigure({
  url: "https://www.example.com/"
});

( window.top ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ถ”๊ฐ€ ์ด์ ์ด ์žˆ์Œ)

์ด์ œ #5003์„ ๋ณ‘ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์„ docblock์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ์„์ง€ ๋ชจ๋ฅด์ง€๋งŒ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. @cpojer? ์ƒˆ๋กœ์šด ์˜ต์…˜์„ ํ†ตํ•ด ์ œ๊ณต๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ testUrl ๋„ ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ด€์‹ฌ์ด ์ถฉ๋ถ„ํ•˜๋‹ค๋ฉด ํ•ด๋‹น ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๋ณต๊ตฌ๋œ ๋ฒ„์ „์„ ํŒจํ‚ค์ง•ํ•˜๊ณ  npm ๋กœ jest-environment-jsdom-global npm ์— ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

URL์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ ์ด์ƒ์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์ด๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด JSDOM์„ ํ™˜๊ฒฝ์— ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); window.location ์™€ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์ œ์•ˆํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Object.defineProperty(window.location, 'href', {
์„ค์ •: newValue => { currentUrl = newValue; },
});
์ด์ „ ๋ฒ„์ „์—์„œ ์ด๊ฒƒ์„ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ๊ณ  ์ด์ œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
์“ฐ๊ธฐ ๊ฐ€๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด true
์ ‘๊ทผ์ž์™€ ์“ฐ๊ธฐ ๊ฐ€๋Šฅ์„ ๋ชจ๋‘ ์ง€์ •ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋˜ ๋‹ค๋ฅธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š”๋ผ๋Š” NPM์— ์ƒˆ ํŒจํ‚ค์ง€๋ฅผ ๋ฐœํ‘œํ–ˆ๋‹ค jest-environment-jsdom-global ์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ ํ•จ๊ป˜ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์— ๋„์›€์ด ๋  ์ˆ˜์žˆ๋Š”, Object.defineProperty .

๋ˆ„๊ตฌ๋“ ์ง€ { writable: true } ๋Œ€ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์˜ˆ๋ฅผ ๋“ค์–ด:

Object.defineProperty(window.location, 'href', { writable: true })

...

Object.defineProperty(window.location, 'hash', { writable: true })

...

Object.defineProperty(window.location, 'search', { writable: true })

@danielbayerlein ์ด ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ง€์ • ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋ฉ”์‹œ์ง€์— ์˜ˆ์ œ๊ฐ€ ํฌํ•จ๋œ url์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

@modestfake ์ด๋ฏธ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์ฝ์—ˆ์œผ๋ฉฐ https://github.com/facebook/jest/issues/5124#issuecomment -352749005๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Jest 21.xx๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ URL ์—†์ด Object.defineProperty(window.location, 'href', { writable: true }) ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. { writable: true } ์žˆ์Šต๋‹ˆ๋‹ค. URL์„ ์„ค์ •ํ•˜๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

@danielbayerlein ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ์žฌ์ •์˜ํ•˜์ง€ ์•Š๋Š” ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์ด๊ฒƒ์„ ์ดํ•ดํ•˜๋ฉด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

URL์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ผ์šฐํŒ….js

...

export function redirectToErrorPage () {
  window.location.href = '/error.html'
}

...

๋ผ์šฐํŒ….test.js

test('redirect to the error page', () => {
  ...
  expect(window.location.href).toBe('/error.html')
  ...
})

Jest 21.xx๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Object.defineProperty(window.location, 'href', { writable: true })

window.location.assign ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•จ์ˆ˜๋ฅผ ๋ชจ์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@simon360 ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๐Ÿค

๋‚˜๋Š” ์‚ฌ์šฉํ–ˆ๋‹ค

history.pushState({}, "page 2", "/bar.html");

jest ๊ตฌ์„ฑ์—์„œ testURL ์™€ ํ•จ๊ป˜

์œ„์น˜ ๋ถ€๋ถ„๋งŒ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. jsdom.reconfigureWindow ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด jsdom ๋ณ„๋„๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค(์ตœ์‹  ๋ฒ„์ „์˜ jsdom์—๋Š” ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ๋” ์ด์ƒ ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค). window !== top ๊ฒฝ์šฐ ๋‹ค๋ฅด๊ฒŒ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์‹  ๋ฒ„์ „์˜ jsdom์„ ์‚ฌ์šฉํ•˜๋Š” ์ตœ์‹  ๋ฒ„์ „์˜ jest์—์„œ๋Š” ๋” ์ด์ƒ ์ด๋ฅผ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

@andyearnshaw jsdom.reconfigure ๋ฉ”์„œ๋“œ์—๋Š” ๊ฐœ์ฒด์— windowTop ๋ฉค๋ฒ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์—์„œ ๋งํฌํ•œ JSDOM ํ™˜๊ฒฝ( jest-environment-jsdom-global )์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

ํ…Œ์ŠคํŠธ์—์„œ ์ตœ๊ณ ์˜ ๊ฐ€์น˜๋ฅผ ์กฐ๋กฑํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

2018๋…„ 1์›” 30์ผ ํ™”, 13:40 simon360, ์•Œ๋ฆผ @github.com ์ž‘์„ฑ:

@andyearnshaw https://github.com/andyearnshaw jsdom.reconfigure
๋ฉ”์„œ๋“œ์˜ ๊ฐœ์ฒด์— windowTop ๋ฉค๋ฒ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

JSDOM ํ™˜๊ฒฝ(jest-environment-jsdom-global)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ I
์œ„์˜ ๋งํฌ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

jsdom.reconfigure({
windowTop: YOUR_VALUE
});

ํ…Œ์ŠคํŠธ์—์„œ ์ตœ๊ณ ์˜ ๊ฐ€์น˜๋ฅผ ์กฐ๋กฑํ•ฉ๋‹ˆ๋‹ค.

โ€”
๋‹น์‹ ์ด ์–ธ๊ธ‰๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋ฐ›๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ๋‹ต์žฅํ•˜๊ณ  GitHub์—์„œ ํ™•์ธํ•˜์„ธ์š”.
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 ๋˜๋Š” ์Œ์†Œ๊ฑฐ
์Šค๋ ˆ๋“œ
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

window.history.pushState ๋ฐ testUrl ์ด ์ €์—๊ฒŒ ํšจ๊ณผ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/facebook/jest/issues/5124#issuecomment -359411593

Jest ์ธก์—์„œ ํ•  ์ผ์ด ์—†๊ณ  ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์œผ๋ฏ€๋กœ ์ด๊ฒƒ์„ ๋‹ซ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค(์ž์œ ๋กญ๊ฒŒ ํ† ๋ก ์„ ๊ณ„์†ํ•˜์‹ญ์‹œ์˜ค!)

jsdom v8.5.0์—์„œ v11.6.2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด package.json ์—๋Š” ๋‹ค์Œ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

"jest": "^21.2.1",
"jest-cli": "^21.2.1",
"jsdom": "^11.6.2",

jest ๋ฐ jest-cli๋ฅผ v22.2.2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ฉด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.

@andr-3-w package.json์— jsdom์ด ์žˆ๋Š” ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? Jest์™€ ํ•จ๊ป˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

@SimenB๋Š” ์งˆ๋ฌธ ์ข‹์€์— ๋Œ€ํ•œ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค jsdom ์šฐ๋ฆฌ์˜ package.json . ๊ฐ์‚ฌ ํ•ด์š”!

๋”ฐ๋ผ์„œ jsdom์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋‚ด ๋ฌผ๊ฑด์— ๋Œ€ํ•ด ์ƒ๊ฐํ•ด๋‚ธ ์†”๋ฃจ์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Jest 21.2.1์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค(์ €๋Š” ํ•ด๋‹น ๋ฒ„์ „์—์„œ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค).

Jest ์„ค์ •์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: package.json ์‚ฌ์šฉ).

"jest": { "testURL": "http://localhost" }

์ด์ œ window.location ๊ฐ์ฒด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ…Œ์ŠคํŠธ ์ค‘์— URL์„ ์›ํ•˜๋Š” ๋Œ€๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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);
});

์ด๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€๋ฅผ ๋•๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ฒŒ์‹œ๋ฅผ ์ค‘์ง€ํ•˜์‹ญ์‹œ์˜ค. ๋†๋‹ด์œผ๋กœ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.": "^22.4.2"

@UserNT ์—ฌ๊ธฐ์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ฒ„์ „์„ ์–ธ๊ธ‰ํ–ˆ์œผ๋ฉฐ ํ”„๋กœ๋•์…˜ ํ…Œ์ŠคํŠธ ์ŠˆํŠธ์— ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋ฒ„์ „์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์ž‘์œ„ ๊ณต๊ฒฉ ๋Œ€์‹  ์ž์‹ ์˜ ์†”๋ฃจ์…˜์„ ์ƒ๊ฐํ•ด ๋ณด์„ธ์š”.

์™„์ „์„ฑ์„ ์œ„ํ•ด ์†”๋ฃจ์…˜์ด ์ด ์Šค๋ ˆ๋“œ์˜ ์ค‘๊ฐ„์— ์ขŒ์ดˆ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—...

@petar-prog91์˜ ์†”๋ฃจ์…˜์€ Jest 21์—์„œ ์ž‘๋™ํ•˜์ง€๋งŒ ์—…๋ฐ์ดํŠธ๋œ jsdom ๊ฐ€ ์žˆ๋Š” Jest 22์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฐ™์€ ์ตœ์‹  ๋†๋‹ด์—์„œ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด, ์‚ฌ์šฉ ๋ญ”๊ฐ€ jest-environment-jsdom-global (์ „์ฒด ๊ณต๊ฐœ, ์ด๊ฒƒ์€ ๋‚ด ํŒจํ‚ค์ง€)์— ๋…ธ์ถœ jsdom ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  jsdom.reconfigure ์žˆ์„ ๊ฒƒ์ด๋‹ค, ๋™์ผํ•œ (๋˜๋Š” ์ ์–ด๋„ ์œ ์‚ฌํ•œ) ํšจ๊ณผ.

https://github.com/facebook/jest/issues/5124#issuecomment -359411593์€ jest 22์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@simon360 ์•ˆ๋…•ํ•˜์„ธ์š”, location.href ์˜ setter๋ฅผ ํ•ดํ‚นํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?
๋‚ด ์ด์ „ ํ…Œ์ŠคํŠธ์—๋Š” ์ด์™€ ๊ฐ™์€ ํ…Œ์ŠคํŠธ๊ฐ€ ๋งŽ์•˜๊ณ  ์ง€๊ธˆ์€ ๋ชจ๋‘ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค...

const setHrefMockFn = jest.fn();
beforeAll(() => {
  Object.defineProperty(location, "href", {
    get: () => "https://xxxxx",
    set: setHrefMockFn
  });
});
it("xxx", () => {
  //...
  expect(setHrefMockFn.mock.calls[0][0]).toBe(xxx);
});

@simon360 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ…Œ์ŠคํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์˜ ์˜ˆ๋ฅผ ๋“ค์–ด

beforeAll(() => {
  =======

  Object.defineProperty(window.location, 'href', {
    writable: true
  });
});

@abhijeetNmishra ๋ฌธ์„œ ๋ฅผ ๋ณด์•˜์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‹น์‹ ์˜ ์งˆ๋ฌธ์— ๋‹ตํ•  ๊ฒƒ์ด๋ผ๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

@ simon360 ์˜ˆ, ๋ฌธ์„œ์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์•ฝ

jsdom.reconfigure({
      url: "https://www.example.com/"
    });

ํ…Œ์ŠคํŠธ๋ณ„๋กœ๊ฐ€ ์•„๋‹ˆ๋ผ ์ „์—ญ์ ์œผ๋กœ URL์„ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๋„์™€์ฃผ์„ธ์š”!

@abhijeetNmishra ์ด ๋ฌธ์ œ๊ฐ€ ํ† ๋ก ํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ข‹์€ ์žฅ์†Œ์ธ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. jest-environment-jsdom-global ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์—ด์–ด์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๊ฐ์‚ฌ ํ•ด์š”!

@SimenB ๋ช…์‹œ๋œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•("use jest-environment-jsdom-global ")์€ ๋ถ„๋ช…ํžˆ ๋งค์šฐ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ทน๋„๋กœ ์ฐจ์„ ์ฑ…์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. Jest 22๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ์‚ฌ๋žŒ์€ ์ด์ œ ํ•ด๋‹น ํƒ€์‚ฌ ํŒจํ‚ค์ง€์— ์ข…์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  (์‚ฌ์šฉ์ž์˜ ๊ด€์ ์—์„œ) ํ…Œ์ŠคํŠธ ์ค‘ ์ผ๋ถ€๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Jest์˜ ํ–‰๋™์ด ํ‡ดํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ธฐ๋ณธ jest-environment-jsdom ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋Š” ์ด์— ๋Œ€ํ•œ ์†”๋ฃจ์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋‹น์‹ ์˜ ์ง€๋„์™€ ํ•จ๊ป˜ PR์„ ํ•˜๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ window.location.href๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด ์ด ๋งŽ์€ ๊ณ ๋ฆฌ๋ฅผ ๊ฑด๋„ˆ๋›ฐ์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋ถˆํ–‰ํ•œ ์ผ์ž…๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ Jest๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์œผ๋ฉฐ ์ด ๋ฌธ์ œ๋ฅผ ๊ณ ๋ คํ•  ๋•Œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ์„ ์žฌ๊ณ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์œ„์—์„œ ์ œ์•ˆํ•œ ๋ชป์ƒ๊ธด ํ•ดํ‚น๋ณด๋‹ค ๋” ๋‚˜์€ ์†”๋ฃจ์…˜์ด ์ •๋ง ์—†์„๊นŒ์š”?

@ydogandjiev ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์ ํŠธ์— ์ž์œ ๋กญ๊ฒŒ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์˜คํ”ˆ ์†Œ์Šค์ด๋ฏ€๋กœ "๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์—†๋‹ค", "์–ด๋ฆฌ์„๋‹ค"์™€ ๊ฐ™์€ ๋Œ“๊ธ€๋กœ ๋‚ ๋›ฐ๋Š” ๊ฒƒ์€ ๋ˆ„๊ตฌ์—๊ฒŒ๋„ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•˜์‹ญ์‹œ์˜ค.

@msholty-fd ํ•  ์ˆ˜๋งŒ ์žˆ๋‹ค๋ฉด ๋•๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ €๋Š” jest์™€ jsdom์„ ๋ง‰ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ์ตœ์„ ์˜ ๋ฐฉ๋ฒ•์ด ๋ฌด์—‡์ธ์ง€ ์ •ํ™•ํžˆ ์•Œ๊ธฐ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ๊นŠ์ด ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋†๋‹ด์ด๋‚˜ jsdom์—์„œ ๊ฐ€์žฅ ์ž˜ ํ•ด๊ฒฐ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ? ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ํ•˜๋‚˜๊ฐ€ Object.defineProperty ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊นจ๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋†๋‹ด์ด๋‚˜ jsdom์—์„œ ๋ณ€๊ฒฝ๋œ ์‚ฌํ•ญ์ž…๋‹ˆ๊นŒ?

๋‹ค์Œ์€ window.location์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ค„ ์ˆ˜์— ๋”ฐ๋ผ ์„ ํ˜ธํ•˜๋Š” ๋ชจ๋“  ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ์ด๋“ค ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ํ˜„์žฌ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

  1. jsdom์—์„œ "์˜ค๋ฅ˜: ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: ํƒ์ƒ‰(ํ•ด์‹œ ๋ณ€๊ฒฝ ์ œ์™ธ)" ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— href๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    window.location.href = "https://www.example.com";
  1. JSDOM์ด ์ฐฝ์˜ ์œ„์น˜ ์†์„ฑ์„ [Unforgeable] ๋กœ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Object.defineProperty๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. jest๋Š” ์‰ฌ์šด ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด ๋…ธ์ถœ๋˜์ง€ ์•Š์€ ์ž์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— jsdom์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    import { JSDOM } from "jsdom";
    ...
    const dom = new JSDOM();
    dom.reconfigure({ url: "https://www.example.com" });
    

์˜ต์…˜ 1 ๋˜๋Š” 2๋ฅผ ์ž‘๋™์‹œํ‚ค๋ ค๋ฉด jsdom์ด ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €์ฒ˜๋Ÿผ ํ–‰๋™ํ•˜๋ ค๋Š” ํ˜„์žฌ ๋ชฉํ‘œ๋ฅผ ์—ญ์ถ”์ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ์˜ต์…˜์€ jest๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” jsdom์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์‰ฝ๊ฒŒ ์žฌ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ์ „์—ญ jest ๊ฐ์ฒด์— ์ง์ ‘ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์ฆ‰, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

jest.dom.reconfigure({ url: "https://www.example.com" });

๋‚˜๋Š” ์—ฌ์ „ํžˆ location.assign('some-url') ๊ฒƒ์ด location.href = 'some-url' ๋ณด๋‹ค ๋‚ซ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ• ๋‹น๋ณด๋‹ค ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ๋” ๋ช…์‹œ์ ์ด๋ฉฐ ํ•จ์ˆ˜๋ฅผ ์กฐ๋กฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ _set_๋ฅผ ์‹œ๋„ํ•˜๋Š” ๊ฒฝ์šฐ์— @SimenB location.href , ์˜ˆ location.assign() ๋” ์ข‹๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ location.href _reads_ location.assign() ๊ฐ€ ์‹ค์ œ๋กœ ์•„๋ฌด ๊ฒƒ๋„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— location.assign() ๊ฐ€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

reconfigure ์‚ฌ์šฉํ•˜๋Š” ์•„์ด๋””์–ด๋Š” location.href ๊ฐ€ ํŠน์ • ๋ฐฉ์‹์œผ๋กœ ํ˜•์„ฑ๋  ๋•Œ๋งŒ ํ™œ์„ฑํ™”๋˜๋Š” ์ฝ”๋“œ ๊ฒฝ๋กœ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ํ˜„์žฌ ๋„๋ฉ”์ธ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ๋œ ์ผ๋ถ€ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๋ƒ„์ƒˆ๊ฐ€ ๋‚˜์ง€๋งŒ ๋˜ํ•œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ƒ„์ƒˆ ๋‚˜๋Š” ์ฝ”๋“œ๋ฅผ ์™„ํ™”ํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ๋™์ž‘์„ ์บก์ฒ˜ํ•˜๊ณ  ์œ ์ง€๋˜๋„๋ก ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์žฅ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ œ์ž๋ฆฌ์—.

๋ฆฌ๋””๋ ‰์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด Enzyme ๋ฐ ํƒ‘์žฌ๋œ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

Jest๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ธฐ ์ „์— ์•„๋ž˜ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค.

```
it('๊ฒฝ๋กœ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒฝ๋กœ', () => {

Object.defineProperty(window.location, 'href', {
  writable: true,
  value: 'https://mysuperawesomesite.com/',
});

const component = mount(
  <App {...props} />
);

const link = component.find('.class');

link.simulate('click');

expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');

});

After upgrading Jest and implementing [jest-environment-jsdom-global](https://www.npmjs.com/package/jest-environment-jsdom-global), I tried the following to no avail:

  ```
it('routes to correct route', () => {

    jsdom.reconfigure({
      url: 'https://mysuperawesomesite.com/',
    });

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');
  });

(window.location.href๋Š” ์—ฌ์ „ํžˆ ' https://mysuperawesomesite.com/ '๊ณผ ๊ฐ™์œผ๋ฉฐ ('https://mysuperawesomesite.com/new')๋กœ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ๋•Œ ์š”์†Œ์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๋Š” ๋ฆฌ๋””๋ ‰์…˜๋˜์ง€ ์•Š์œผ๋ฉฐ ๋ฆฌ๋””๋ ‰์…˜์€ window.location.href๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์ ์ ˆํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ• ๋˜๋Š” ์ด์ „์— Object.defineProperty๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ œ๋Œ€๋กœ ๊ตฌ์„ฑ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ๋ถˆ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. ๋„์›€์„ ์ฃผ์…”์„œ ๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

ํŽธ์ง‘: ํ•ด๊ฒฐ

window.location.href = href ๋Œ€์‹  window.location.assign(url)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด assign ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ œ๋Œ€๋กœ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฐธ์กฐ:

it('routes to correct route', () => {
    window.location.assign = jest.fn();

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.assign).toBeCalledWith('https://mysuperawesomesite.com/new');
    window.location.assign.mockRestore();
  });

@SimenB .assign ์™€ .href ์‚ฌ์ด์—๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. MDN์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์ฃผ์š” ๊ต์ฐจ ๋„๋ฉ”์ธ ์ œํ•œ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์ฝ”๋“œ์—์„œ ๋‚ด ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ ์ค‘์ธ iframe์—์„œ ์ƒ์œ„ ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ ๊ต์ฐจ ๋„๋ฉ”์ธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚ด๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ href ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
ํ˜„์žฌ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ํ‘œ์‹œ๋˜์ง€ ์•Š๊ณ  ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ์—ด๋ฆฌ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ์งœ์ฆ๋‚ฉ๋‹ˆ๋‹ค.

@soswow์™€ ๊ฐ™์€ ๋ฐฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์ด ๋ณต์›๋  ๋•Œ๊นŒ์ง€ ์—ฌ๋Ÿฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋„ ์ œ๊ฑฐํ•˜๋ฏ€๋กœ URL์„ ์žฌ์ •์˜ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ํ‘œ์‹œ๋˜์ง€ ์•Š๊ณ  ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ์—ด๋ฆฌ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ์งœ์ฆ๋‚ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ๋†๋‹ด์˜ ํŽธ์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์€ ์•„๋ฌด๊ฒƒ๋„ ์—†์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” jsdom์ด ๊ทธ๊ฒƒ์„ ์ง€์›ํ•˜๋Š” PR์„ ์ข‹์•„ํ•  ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. https://github.com/jsdom/jsdom/issues/2112

๋‹ค์Œ์€ ์ž‘๋™ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค.

describe('changing location', () => {
  const testURL = location.href;

  beforeEach(() => history.replaceState({}, 'Login', '/login'));
  afterEach(() => history.replaceState({}, 'Home', '/'));

  it('works', () => {
    expect(location.pathname).toBe('/login');
  });
});

@vastus ๋‚ด๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ ํ–ˆ๋“ฏ์ด ๋ฌธ์ œ๋Š” ๊ต์ฐจ ๋„๋ฉ”์ธ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ธฐ์–ตํ•˜๋Š” ํ•œ history API๋Š” ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์œผ๋กœ์˜ ์ „ํ™˜์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

location ๋Š” jsdom window ๊ฐœ์ฒด์—์„œ ์ง์ ‘ ์žฌ์ •์˜ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ•œ ๊ฐ€์ง€ ๊ฐ€๋Šฅํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํŒŒ์ƒ ๊ฐœ์ฒด์—์„œ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

๋‚˜๋Š” ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

const windowLocation = JSON.stringify(window.location);
delete window.location;
Object.defineProperty(window, 'location', {
  value: JSON.parse(windowLocation)
});

@RubenVerborgh ๋ฐ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค .

@vastus ์†”๋ฃจ์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ location.search ํ…Œ์ŠคํŠธ ์˜ˆ์ œ ์ถ”๊ฐ€:

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

@RubenVerborgh ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋…ธ๋ ฅํ•˜๋‹ค:

window.history.pushState({}, null, '/pathname?k=v');

@sahalsaad ์™€ ์œ ์‚ฌํ•œ ์†”๋ฃจ์…˜:
```์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ
const oldWindow = window.location;
window.location ์‚ญ์ œ;
์ฐฝ ์œ„์น˜ = {
... ์˜ค๋ž˜๋œ ์ฐฝ,
// ๋‹ค์Œ sinon ์Šคํ…๊ณผ ๊ฐ™์€ ์‚ฌ์šฉ์ž ์ •์˜ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
๊ต์ฒด: sinon.stub(),
};

// ๋งˆ๋ฒ•์„ ๊ฑธ์–ด๋ผ

window.location = ์˜ค๋ž˜๋œ ์ฐฝ;
````

@sahalsaad ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋‚˜๋Š” window.location.search๋ฅผ ์กฐ๋กฑํ•˜๊ธฐ ์œ„ํ•ด ์†”๋ฃจ์…˜์˜ ๋ณ€ํ˜•์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

const location = {
    ...window.location,
    search: queryString,
};
Object.defineProperty(window, 'location', {
    writable: true,
    value: location,
});

์•„๋งˆ๋„ ๋” ๋‚˜์€ ์†”๋ฃจ์…˜:

import { URL } from 'whatwg-url';

const location = new URL(window.location.href);
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()

delete window.location
window.location = location

@kdelmonte๊ฐ€ ์ œ๊ณตํ•œ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. window.location.search ๋ณ€์ˆ˜๋ฅผ ์กฐ๋กฑํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ์‚ฌ์šฉํ–ˆ๋‹ค

window.history.pushState({}, null, '?skuId=1234')

location ๋Š” jsdom window ๊ฐœ์ฒด์—์„œ ์ง์ ‘ ์žฌ์ •์˜ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ•œ ๊ฐ€์ง€ ๊ฐ€๋Šฅํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํŒŒ์ƒ ๊ฐœ์ฒด์—์„œ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

๊ท€ํ•˜์˜ ๋‹ต๋ณ€์€ ๋‚ด ์ƒํ™ฉ์— ๋งž๋Š” ์œ ์ผํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

location ๋Š” jsdom window ๊ฐœ์ฒด์—์„œ ์ง์ ‘ ์žฌ์ •์˜ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ•œ ๊ฐ€์ง€ ๊ฐ€๋Šฅํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํŒŒ์ƒ ๊ฐœ์ฒด์—์„œ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

๊ท€ํ•˜์˜ ๋‹ต๋ณ€์€ ๋‚ด ์ƒํ™ฉ์— ๋งž๋Š” ์œ ์ผํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ด๊ฒƒ์€ ๋” ์ด์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ๐Ÿ˜ข

location ๋Š” jsdom window ๊ฐœ์ฒด์—์„œ ์ง์ ‘ ์žฌ์ •์˜ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ•œ ๊ฐ€์ง€ ๊ฐ€๋Šฅํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ํŒŒ์ƒ ๊ฐœ์ฒด์—์„œ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

global.window = Object.create(window);
Object.defineProperty(window, 'location', {
  value: {
    href: 'http://example.org/'
  }
});

๊ท€ํ•˜์˜ ๋‹ต๋ณ€์€ ๋‚ด ์ƒํ™ฉ์— ๋งž๋Š” ์œ ์ผํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ด๊ฒƒ์€ ๋” ์ด์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ๐Ÿ˜ข

๋‚˜์—๊ฒŒ๋„ ํšจ๊ณผ๊ฐ€ ์—†๋‹ค

๋‚˜๋Š” ์ด๊ฒƒ์„ ํ•ด๊ฒฐํ–ˆ๋‹ค :

delete window.location
window.location = {
  href: 'http://example.org/,
}

Location ์ž‘์—…์„ ์œ„ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๋‹ค์Œ ๋ชจ์˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

export class MockLocation extends URL implements Location {
  ancestorOrigins: any = []
  toString = jest.fn().mockImplementation(() => this.toString())
  assign = jest.fn(href => this.href = href)
  replace = jest.fn(href => this.href = href)
  reload = jest.fn()

  constructor(
    url: string = 'http://mock.localhost',
  ) {
    super(url)
  }

  onWindow(window: Window) {
    Object.defineProperty(window, 'location', { 
      writable: true,
      value: this
    });
    return this
  }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‚ด ํ…Œ์ŠคํŠธ์—์„œ

let location: MockLocation

beforeEach(() => {
    location = new MockLocation(MOCK_PARTNER_URL).onWindow(window)
})

๋‚˜๋Š” ํ•ญ์ƒ ์ด์™€ ๊ฐ™์€ ๊นŒ๋‹ค๋กœ์šด ๊ฐ์ฒด๋ฅผ ์Šคํ„ฐ๋น™ํ•˜๊ณ  ์œ ์—ฐํ•œ ๋„์šฐ๋ฏธ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

export const safelyStubAndThenCleanup = (target, method, value) => {
  const original = target[method]
  beforeEach(() => {
    Object.defineProperty(target, method, { configurable: true, value })
  })
  afterEach(() => {
    Object.defineProperty(target, method, { configurable: true, value: original })
  })
}

๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ๋ฒ•:

describe('when on /pages', () => {
  safelyStubAndThenCleanup(window, 'location', { pathname: '/pages' })

  it('should do something neat', () => { /* ... */ })
})

๊ทธ๋ฆฌ๊ณ  ์›ํ•˜๋Š” ๊ฒƒ์„ ์Šคํ…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. pathname , href ๋“ฑ... ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ •๋ฆฌ์˜ ์ถ”๊ฐ€ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ์€ location ์ž์ฒด๋ฅผ ์—‰๋ง์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์œผ๋ฏ€๋กœ location ๋ฅผ ๊ฐ€์งœ๋กœ ๋ฐ”๊พธ๊ณ  ํ…Œ์ŠคํŠธ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ๋„ฃ์œผ์‹ญ์‹œ์˜ค.

๋””๋ฒ„๊น… ์„ธ์…˜์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด global.location ๋Š” getter๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„๋˜์ง€๋งŒ ๋‹จ์ˆœ ์†์„ฑ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜์ง€ ์•Š์„๊นŒ์š”?

let originalLocationDescriptor;
beforeAll(() => {
  originalLocationDescriptor = Object.getOwnPropertyDescriptor(global, 'location');
  delete global.location;
  global.location = {};
});
afterAll(() => {
  Object.defineProperty(global, 'location', originalLocationDescriptor);
}):

์›๋ž˜ global.location ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋Š” ์ด์œ ๋ฅผ ์ƒ์ƒํ•˜๊ธฐ ์–ด๋ ต์ง€๋งŒ ์กฐ๊ธˆ ๋” ์ •ํ™•ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.
๋ฌผ๋ก  ์ด ์ฝ”๋“œ๋Š” ์ €์—๊ฒŒ ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹จ์ง€ location.pathname ์— ์ ‘๊ทผํ•˜์ง€๋งŒ ์ด ๊ฐ์ฒด๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ jest.fn() ๋กœ ์‰ฝ๊ฒŒ ํ™•์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ jest 26.5๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ €์—๊ฒŒ ํšจ๊ณผ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

function stubLocation(location) {
  beforeEach(() => {
    jest.spyOn(window, "location", "get").mockReturnValue({
      ...window.location,
      ...location,
    });
  });
}

stubLocation({ pathname: "/facebook/jest/issues/5124" });

test("mocks location prop", () => {
  expect(window.location.pathname).toEqual("/facebook/jest/issues/5124");
});

@vastus ์†”๋ฃจ์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ location.search ํ…Œ์ŠคํŠธ ์˜ˆ์ œ ์ถ”๊ฐ€:

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ๊ฒช๊ณ ์žˆ๋Š” ๋ฌธ์ œ์— ์ •ํ™•ํžˆ ์ž˜ ๋งž์•˜์Šต๋‹ˆ๋‹ค.

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