Jest: Tidak dapat mengubah window.location menggunakan Object.defineProperty

Dibuat pada 19 Des 2017  ·  78Komentar  ·  Sumber: facebook/jest

Apakah Anda ingin meminta _fitur_ atau melaporkan _bug_? Laporkan bug

Apa perilaku saat ini?

Memanggil yang berikut ini dari dalam test suite:

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

melempar kesalahan berikut:

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

Apa perilaku yang diharapkan?

Kode tidak boleh mengeluarkan pengecualian, dan window.location.hostname === "example.com" harus bernilai benar.

Dari kelihatannya, jsdom sekarang menetapkan window.location menjadi unforgeable . Satu-satunya cara untuk mengubah nilai dalam window.location adalah dengan menggunakan reconfigure , tetapi (per #2460) Jest tidak mengekspos jsdom untuk pengujian untuk dimainkan.

Harap berikan konfigurasi Jest Anda yang tepat dan sebutkan Jest, node,versi benang/npm dan sistem operasi.

Versi lelucon: 22.0.1
Versi simpul: 8.6.0
Versi benang : 1.2.0
OS : macOS Sierra Tinggi 10.13.2

Discussion Wontfix

Komentar yang paling membantu

Saya telah menerbitkan paket baru di npm bernama jest-environment-jsdom-global , yang dapat membantu mengatasi masalah yang dialami beberapa orang dengan Object.defineProperty .

Semua 78 komentar

Saya memiliki masalah serupa. Anda dapat membuat JSDOMEnvironment Anda sendiri dan mengekspos objek jsdom ke global seperti ini.

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

Dan kemudian Anda dapat memanggil jsdom.reconfigure dalam test case Anda sesuka Anda

Itu solusi yang bagus, terima kasih telah berbagi!

Anda harus mengembalikan super.teardown(); karena ini adalah janji, btw

Sempurna, @oliverzy - Saya akan mencobanya. Terima kasih!

Apakah ada tempat yang tepat untuk mendokumentasikan ini? Tampaknya itu adalah pertanyaan yang cukup sering muncul; mudah-mudahan, masalah di masa depan dapat dikurangi jika ini diintegrasikan dalam dokumen?

Solusi ini tidak cukup berhasil.

Di dalam file pengujian kami, sepertinya global diatur menjadi objek window JSDom.

Dengan kata lain, di dalam test suite, global sama dengan window , tetapi di dalam kelas yang diperluas JSDOMEnvironment , global berasal dari lingkungan Node.

Akibatnya, memiliki ini:

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

gagal karena global.jsdom tidak ditentukan.

Saya menyiasatinya dengan melakukan ini, tetapi saya tidak terlalu mempermasalahkannya.

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

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

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

Dengan lingkungan ini, global.jsdom di dalam test suite sama dengan this.dom , dan test suite di atas berfungsi.

Bagi saya, rasanya seperti menetapkan jsdom menjadi properti dari objek window nya sendiri

anda perlu menulis jsdom daripada global.jsdom dalam pengujian Anda.

@oliverzy Suka ini?

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

Itu melempar jsdom is not defined , tapi saya mungkin salah mengartikan.

@simon360 Silakan konfigurasikan testEnvironment dengan kode dari @oliverzy , lihat https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

@danielbayerlein konfigurasi Jest saya memiliki ini:

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

di mana @wel-ui/jest-environment-jsdom-global adalah nama paket di monorepo kami. Lingkungan digunakan dengan benar, karena solusi yang menetapkan jsdom pada window berfungsi seperti yang diharapkan.

BTW, adakah yang tahu mengapa solusi asli tidak berfungsi di versi baru?
Yang ini:

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

@modestfake kami telah memutakhirkan dari JSDOM@11 , tebakan saya adalah mereka mengubah cara variabel didefinisikan

@SimenB Mengerti. Baru saja menemukan deskripsi metode jsdom reconfigure .

Properti teratas di jendela ditandai [Unforgeable] di spec, yang berarti itu adalah properti sendiri yang tidak dapat dikonfigurasi dan dengan demikian tidak dapat ditimpa atau dibayangi oleh kode normal yang berjalan di dalam jsdom, bahkan menggunakan Object.defineProperty.

Saya menambahkan repositori baru untuk menunjukkan perilaku ini. Adakah yang bisa mereproduksinya dengan mengkloning secara lokal?

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

@simon360 direproduksi
image

@simon360 saya telah menemukan. Anda melewatkan kata kunci this saat menentukan global.jsdom :

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

Bagaimana dengan location.search ? Saya tidak menemukan penyebutan tentang itu https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

@andrewBalekha Bagaimana dengan ini?

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

Terima kasih @modestfake - maaf atas kesalahan bodohnya!

Oke, saya melihatnya sekarang - this.global pada objek lingkungan Jest ditetapkan sebagai global dalam file uji Jest. Itu masuk akal - terima kasih telah membantu saya melewatinya! Jika ada cukup minat, saya dapat mengemas versi yang diperbaiki dari repo itu dan meletakkannya di npm sebagai jest-environment-jsdom-global .

Namun, saya berharap ada cara yang lebih bersih untuk melakukan ini di Jest di masa mendatang. Ini bukan cara gesekan rendah untuk mengubah window.location -

Mungkinkah ada docblock baru, seperti yang ada untuk @jest-environment ? Sebagai contoh...

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

Atau, mungkin JSDom dapat diekspos pada bagian khusus dari objek jest - sesuatu seperti:

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

(yang akan memiliki manfaat tambahan karena dapat mengubah window.top )

Kami telah menggabungkan #5003 sekarang. bisa menambahkannya sebagai docblock mungkin masuk akal, tidak yakin. @cpojer? Kami juga dapat menghentikan testUrl , karena dapat diberikan melalui opsi baru itu.

Jika ada cukup minat, saya dapat mengemas versi yang diperbaiki dari repo itu dan meletakkannya di npm sebagai jest-environment-jsdom-global .

Saya pikir itu masuk akal dalam hal apa pun, karena ini lebih dari sekadar membiarkan Anda mengatur url - ini memaparkan JSDOM penuh ke lingkungan

@andrewBalekha Object.defineProperty(location, 'search', { ...options }); melempar kesalahan yang sama dengan window.location . Terima kasih atas sarannya.

Object.defineProperty(jendela.lokasi, 'href', {
set: nilai baru => { currentUrl = nilai baru; },
});
Saya memiliki ini di versi sebelumnya dan sekarang melempar kesalahan.
Jika saya menambahkan yang dapat ditulis: benar
melempar pengecualian lain yang tidak dapat saya tentukan baik pengakses maupun dapat ditulis

Saya telah menerbitkan paket baru di npm bernama jest-environment-jsdom-global , yang dapat membantu mengatasi masalah yang dialami beberapa orang dengan Object.defineProperty .

Adakah yang punya solusi untuk { writable: true } ?

Sebagai contoh:

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

...

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

...

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

@danielbayerlein membaca utas ini. Anda perlu membuat lingkungan khusus. Pesan sebelumnya berisi url dengan contoh

@modestfake Saya sudah membaca utas ini dan https://github.com/facebook/jest/issues/5124#issuecomment -352749005 berfungsi dengan baik. Tapi saya punya kasus penggunaan lain. Dengan Jest 21.xx saya telah menetapkan Object.defineProperty(window.location, 'href', { writable: true }) tanpa URL - hanya { writable: true } . Jika saya mengatur URL, maka tesnya tidak masuk akal.

@danielbayerlein apa kasus penggunaan untuk membuatnya dapat ditulis tetapi sebenarnya tidak

Saya memiliki fungsi yang mengubah URL.

routing.js

...

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

...

routing.test.js

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

Dengan Jest 21.xx saya telah menetapkan Object.defineProperty(window.location, 'href', { writable: true })

Saya akan merekomendasikan beralih ke window.location.assign , dengan begitu Anda dapat mengejek fungsinya.

@simon360 Bekerja seperti pesona! Terima kasih. 🤝.

saya menggunakan

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

bersama dengan testURL dalam konfigurasi lelucon

Bagian lokasi bukan satu-satunya masalah. Saya tidak dapat mengimpor jsdom secara terpisah untuk memanggil jsdom.reconfigureWindow (karena fungsi itu tidak ada lagi di versi terbaru jsdom). Saya melakukan ini untuk menguji kode yang berjalan secara berbeda jika window !== top . Tidak ada lagi cara untuk mencapai ini di versi terbaru dari lelucon, yang menggunakan versi jsdom yang lebih baru.

@andyearnshaw metode jsdom.reconfigure memiliki anggota windowTop dalam objeknya.

Jika Anda menggunakan lingkungan JSDOM ( jest-environment-jsdom-global ) yang saya tautkan di atas, Anda dapat melakukan:

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

dalam pengujian Anda untuk mengejek nilai tertinggi.

Saya menggunakan itu dan itu berfungsi dengan baik, terima kasih!

Pada Sel, 30 Jan 2018, 13:40 simon360, [email protected] menulis:

@andyearnshaw https://github.com/andyearnshaw jsdom.reconfigure
metode memiliki anggota windowTop di objeknya.

Jika Anda menggunakan lingkungan JSDOM (jest-environment-jsdom-global) saya
ditautkan di atas, Anda dapat melakukan:

jsdom.konfigurasi ulang({
windowTop: YOUR_VALUE
});

dalam pengujian Anda untuk mengejek nilai tertinggi.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 , atau bisu
benang
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

Menggunakan window.history.pushState dan testUrl berhasil untuk saya

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

Akan menutup ini karena tidak ada yang bisa dilakukan di pihak Jest, dan ada solusi (jangan ragu untuk melanjutkan diskusi!)

Memutakhirkan dari jsdom v8.5.0 ke v11.6.2 memecahkan masalah bagi saya. Jadi package.json termasuk:

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

Rusak jika saya memutakhirkan jest dan jest-cli ke v22.2.2.

@andr-3-w ada alasan khusus mengapa Anda memiliki jsdom di package.json Anda? Itu datang dibundel dengan Jest.

@SimenB , pertanyaan bagus, tidak perlu jsdom di package.json . Terima kasih!

Jadi, tanpa menggunakan jsdom secara langsung, inilah solusi yang saya buat untuk barang-barang saya:

Bekerja untuk Jest 21.2.1 (saya menguji yang itu):

Masuk ke pengaturan Jest Anda (misalnya saya akan menggunakan package.json):

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

Sekarang Anda akan dapat mengubah objek window.location dan kemudian Anda dapat mengatur URL ke apa pun yang Anda suka selama pengujian.

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

Semoga ini bisa membantu seseorang.

Berhenti memposting ini, ini tidak berfungsi pada lelucon": "^22.4.2"

@UserNT Saya mencatat versi yang berfungsi di sini dan saya menggunakannya pada pengujian produksi yang sesuai secara ekstensif. Jika tidak berfungsi pada versi yang lebih baru, saya minta maaf, buat solusi Anda sendiri alih-alih hanya bashing acak.

Sekedar kelengkapan, karena solusinya terdampar di tengah thread ini...

Solusi @petar-prog91 akan bekerja pada Jest 21, tetapi tidak pada Jest 22 dengan jsdom diperbarui.

Untuk menjalankan Jest terbaru, gunakan sesuatu seperti jest-environment-jsdom-global (pengungkapan penuh, ini adalah paket saya) untuk mengekspos objek jsdom dan gunakan jsdom.reconfigure , yang akan memiliki efek yang sama (atau, setidaknya, serupa).

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 juga berfungsi pada lelucon 22 untuk saya

@simon360 Hai, apa yang harus saya lakukan jika saya perlu meretas setter location.href ?
Tes saya sebelumnya memiliki banyak tes seperti ini dan sekarang semuanya gagal ...

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 dapatkah Anda memberi saya contoh cara memperbarui tes seperti di bawah ini dengan perpustakaan Anda

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

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

@abhijeetNmishra apakah Anda sudah melihat dokumentasinya ? Saya percaya itu akan menjawab pertanyaan Anda.

@simon360 Ya, berdasarkan pemahaman saya tentang dokumentasi, dibutuhkan sekitar

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

yang menimpa url secara global dan bukan per pengujian. Tolong bantu!

@abhijeetNmishra Saya tidak yakin bahwa masalah ini adalah tempat terbaik untuk berdiskusi. Maukah Anda membuka masalah pada repositori jest-environment-jsdom-global tempat kami dapat mengatasinya? Terima kasih!

@SimenB solusi yang dinyatakan ("gunakan jest-environment-jsdom-global ") terasa seperti solusi yang sangat suboptimal untuk apa yang jelas merupakan masalah yang sangat umum. Siapa pun yang memutakhirkan ke Jest 22 sekarang perlu menambahkan ketergantungan ke paket pihak ketiga itu dan (dari sudut pandang pengguna) menulis ulang beberapa pengujian mereka. Ini adalah regresi dalam perilaku Jest.

Apakah ada solusi untuk ini yang dapat kita bangun ke default jest-environment-jsdom ? Senang membuat PR dengan bimbingan Anda.

Sangat disayangkan bahwa seseorang harus melompati banyak rintangan ini hanya untuk mengubah window.location.href. Saya baru saja mulai menggunakan Jest dan saya akan mempertimbangkan kembali pilihan kerangka pengujian saya mengingat masalah ini. Apakah benar-benar tidak ada solusi yang lebih baik daripada peretasan jelek yang disarankan di atas?

@ydogandjiev merasa bebas untuk berkontribusi pada proyek untuk memecahkan masalah ini. Ingatlah bahwa ini adalah open source, jadi mengamuk dengan komentar seperti "tidak dapat diterima" dan "konyol" tidak membantu siapa pun.

@msholty-fd Saya ingin membantu jika saya bisa. Karena saya baru memulai dengan lelucon dan jsdom, saya tidak yakin bahwa saya memiliki pemahaman yang cukup mendalam tentang perpustakaan ini untuk mengetahui dengan tepat apa rute terbaik untuk meningkatkan pengalaman ini. Apakah ini sesuatu yang paling baik dibahas dalam lelucon atau jsdom? Sepertinya salah satu perpustakaan ini membuat perubahan di beberapa titik yang mematahkan pendekatan Object.defineProperty; apakah itu perubahan yang dibuat dalam lelucon atau jsdom?

Jadi, inilah semua opsi yang saya anggap lebih disukai berdasarkan jumlah baris yang diperlukan untuk mengubah window.location; tidak ada yang saat ini berfungsi:

  1. Menyetel href secara langsung tidak berfungsi karena jsdom memberikan pengecualian dengan pesan "Kesalahan: Tidak diterapkan: navigasi (kecuali perubahan hash)":
    window.location.href = "https://www.example.com";
  1. Menggunakan Object.defineProperty tidak berfungsi karena JSDOM telah membuat properti lokasi jendela [Unforgeable] :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. Membuat instance jsdom dan mengonfigurasinya tidak berfungsi karena lelucon tampaknya menggunakan instance-nya sendiri yang tidak diekspos untuk akses mudah:

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

Membuat opsi 1 atau 2 berfungsi akan membutuhkan jsdom untuk mundur pada tujuan mereka saat ini untuk berperilaku lebih seperti browser nyata. Oleh karena itu, sepertinya satu-satunya pilihan yang kita miliki adalah membuatnya lebih mudah untuk mengonfigurasi ulang instance jsdom yang digunakan lelucon. Apakah masuk akal untuk mengekspos contoh itu secara langsung pada objek lelucon global; yaitu mengizinkan sesuatu seperti ini:

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

Saya masih berpikir melakukan location.assign('some-url') lebih baik daripada location.href = 'some-url' . Saya menemukan panggilan fungsi lebih eksplisit daripada penugasan, dan Anda dapat mengejek fungsinya

@SimenB dalam kasus di mana kode mencoba _set_ location.href , ya, location.assign() lebih baik. Tetapi jika Anda menguji perilaku yang _reads_ location.href , location.assign() tidak menyelesaikan masalah, terutama karena location.assign() di JSDOM sebenarnya tidak melakukan apa pun.

Ide dibalik penggunaan reconfigure adalah untuk mengaktifkan jalur kode yang hanya diaktifkan ketika location.href dibentuk dengan cara tertentu. Dalam kasus kami, kami memiliki beberapa kode yang berubah tergantung pada domain saat ini - kode tersebut bau, ya, tetapi itu juga perlu, dan cara terbaik untuk mengurangi kode bau adalah dengan memiliki perlengkapan pengujian yang menangkap perilaku dan memastikan bahwa itu tetap ada di tempat.

Apakah ada cara untuk menggunakan ini dengan Enzim dan komponen yang dipasang untuk menguji pengalihan?

Tes di bawah ini lulus sebelum memutakhirkan Jest:

```
it('rute ke rute yang benar', () => {

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 masih sama dengan ' https://mysuperawesomesite.com/ ', tidak diubah menjadi ('https://mysuperawesomesite.com/new').

Peristiwa klik pada elemen tidak mengalihkan saat menggunakan metode ini, dan pengalihan terjadi dengan menyetel window.location.href.

Tidak jelas tentang cara menguji ini dengan benar atau apakah pengujian yang sebelumnya menggunakan Object.defineProperty dibangun dengan buruk untuk memulai. Terima kasih sebelumnya atas bantuan apa pun.

EDIT: TERSELESAIKAN

Mampu menyelesaikan ini dengan menggunakan window.location.assign(url) alih-alih window.location.href = href. Ini memungkinkan saya untuk mematikan metode penetapan dan menguji apakah itu dipanggil dengan benar. Lihat di bawah:

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 Ada perbedaan besar antara .assign dan .href Anda dapat membaca di MDN. Yang pertama memiliki batasan lintas domain utama. Dalam kode saya, saya ingin mengarahkan ulang halaman induk dari iframe tempat kode saya berjalan. Itu adalah lintas domain. Dan satu-satunya cara saya bisa melakukan ini adalah dengan mengubah href .
Saya akan senang jika masalah ini dibuka kembali, karena saya tidak melihat solusi saat ini dan saya harus tidak melakukan tes untuk fungsi ini. Yang jelas menyebalkan.

Saya senasib dengan @soswow. Akan sangat bagus jika Anda dapat menyediakan mekanisme untuk mengganti url, karena saya juga menghapus beberapa pengujian unit hingga fungsi ini dipulihkan.

Saya akan senang jika masalah ini dibuka kembali, karena saya tidak melihat solusi saat ini dan saya harus tidak melakukan tes untuk fungsi ini. Yang jelas menyebalkan.

Tidak ada yang bisa kita lakukan di sisi lelucon. Saya yakin jsdom akan menyukai PR yang mendukungnya. https://github.com/jsdom/jsdom/issues/2112

Inilah solusi sederhana yang berhasil.

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

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

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

@vastus Seperti yang saya tunjukkan secara eksplisit - masalahnya adalah dengan cross-domains . history API tidak akan mengizinkan beralih ke domain yang berbeda sejauh yang saya ingat.

Karena location tidak dapat ditimpa secara langsung pada objek jsdom window , satu pendekatan yang mungkin adalah menimpanya pada objek turunan:

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

Saya menggunakan ini untuk menyelesaikan masalah:

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

Terinspirasi oleh @RubenVerborgh & annemarie35

Menambahkan contoh pengujian location.search berdasarkan solusi @vastus :

  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 Bekerja seperti pesona.

mencoba:

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

Solusi yang mirip dengan @sahalsaad :
```javascript
const oldWindow = jendela.lokasi;
hapus jendela.lokasi;
jendela.lokasi = {
...jendela lama,
// sertakan custom overwrites seperti sinon stub berikut
ganti: sinon.stub(),
};

// lakukan sihirmu

jendela.lokasi = jendela lama;
````

@sahalsaad terima kasih! Saya menggunakan variasi solusi Anda untuk mengejek window.location.search:

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

mungkin solusi yang lebih baik:

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

Memecahkan masalah saya menggunakan solusi yang diberikan oleh @kdelmonte , saya harus mengejek variabel window.location.search . jadi saya sudah menggunakan

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

Karena location tidak dapat ditimpa secara langsung pada objek jsdom window , satu pendekatan yang mungkin adalah menimpanya pada objek turunan:

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

jawaban Anda adalah satu-satunya yang sesuai untuk situasi saya, terima kasih!

Karena location tidak dapat ditimpa secara langsung pada objek jsdom window , satu pendekatan yang mungkin adalah menimpanya pada objek turunan:

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

jawaban Anda adalah satu-satunya yang sesuai untuk situasi saya, terima kasih!

Ini tidak lagi berfungsi

Karena location tidak dapat ditimpa secara langsung pada objek jsdom window , satu pendekatan yang mungkin adalah menimpanya pada objek turunan:

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

jawaban Anda adalah satu-satunya yang sesuai untuk situasi saya, terima kasih!

Ini tidak lagi berfungsi

Tidak bekerja untuk saya juga

Saya memecahkan ini dengan melakukan:

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

Saya menggunakan tiruan berikut sebagai utilitas untuk bekerja dengan 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
  }
}

Kemudian dalam tes saya

let location: MockLocation

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

Saya mendapati diri saya mematikan objek rumit seperti ini sepanjang waktu, dan membuat fungsi pembantu yang fleksibel:

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

Dan kemudian penggunaan:

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

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

Dan Anda dapat mematikan apa pun yang Anda inginkan: pathname , href , dll... Ini memberi Anda manfaat pembersihan tambahan gratis.

Kuncinya adalah Anda tidak bisa main-main dengan location itu sendiri, jadi tukar saja location dengan yang palsu, lalu pasang kembali setelah tes selesai.

Seperti yang saya lihat di sesi debugging saya, global.location diimplementasikan melalui pengambil tetapi bukan properti sederhana. Bukankah lebih aman untuk mendefinisikan ulang seperti ini?

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

Meskipun sulit untuk membayangkan mengapa saya ingin menggunakan yang asli global.location , tampaknya sedikit lebih benar.
Dan kode ini berfungsi dengan baik untuk saya, tentu saja. Saya baru saja mengakses location.pathname , tetapi objek ini dapat dengan mudah diperluas dengan beberapa jest.fn() jika diperlukan.

Ini berhasil untuk saya, menggunakan lelucon 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");
});

Menambahkan contoh pengujian location.search berdasarkan solusi @vastus :

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

Ini bekerja dengan sangat baik untuk masalah yang saya alami

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

kgowru picture kgowru  ·  3Komentar

stephenlautier picture stephenlautier  ·  3Komentar

paularmstrong picture paularmstrong  ·  3Komentar

ianp picture ianp  ·  3Komentar

hramos picture hramos  ·  3Komentar