Jest: window.location.href tidak dapat diubah dalam pengujian.

Dibuat pada 13 Apr 2016  ·  70Komentar  ·  Sumber: facebook/jest

Hai @cpojer ,

Ini sebenarnya lebih merupakan masalah jsdom@8 ... lihat tmpvar/jsdom#1388, tapi saya ingin menyematkan di sini juga jadi Jest mengambil solusi apa pun yang diberikan jsdom.

Sebelumnya dengan [email protected]/[email protected] Anda dapat menulis tes seperti ini:

jest.autoMockOff()
jest.setMock('../lib/window', window)

jest.mock('cookies-js')
jest.mock('query-string')
jest.mock('superagent')

describe(['@utils/auth - When an AJAX response returns:'
].join('')
, function () {

  beforeEach(function () {
    window.location.href = 'http://quux.default.com'
    var queryString = require('query-string')
    queryString.__setMockParseReturns([{
      'access_token': '1234foo',
      'expires_in': '9999'
    }])
  })

  it(['should set a redirect token and goto platform ',
    'when the AJAX request returns 401.'
  ].join('')
  , function () {
    var superagent = require('superagent')
    superagent.__setMockAjaxResponses([
      [null, { 'status': 401 }]
    ])

    var href = window.location.href
    var auth = require('../index.js')
    auth.login(function (res) {})
    var Cookies = require('cookies-js')
    var config = require.requireActual('../config')
    expect(decodeURIComponent(window.location.href)).toBe([
      config.loginUrl,
      config.loginServiceLogin,
      '?client_id=',
      config.clientId,
      '&response_type=token',
      '&redirect_uri=',
      config.clientRedirectUri
    ].join(''))
    expect(Cookies.__getMockCookieData()[config.clientId + '_state_locationAfterLogin']).toBe(escape(href))
  })

Dan ujian itu akan berlalu. Sejak jsdom@8 ini tidak mungkin lagi dan tes ini gagal.

Sepertinya jsdom sedang melihat beberapa jenis kemampuan, hanya ingin memastikan bahwa Jest akan mengambil kemampuan itu ketika tersedia.

Komentar yang paling membantu

Anda benar, ini memang masalah jsdom. Di Facebook, apa yang telah kami lakukan untuk mengatasinya, adalah menggunakan ini:

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

ini berfungsi untuk kami, namun kami masih menggunakan jsdom 7 secara internal.

Saya akan menutup ini, karena saya percaya cara Object.defineProperty dalam melakukan sesuatu baik-baik saja. Jika itu tidak berhasil untuk Anda di jsdom 8, saya senang untuk membukanya kembali.

Semua 70 komentar

Anda benar, ini memang masalah jsdom. Di Facebook, apa yang telah kami lakukan untuk mengatasinya, adalah menggunakan ini:

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

ini berfungsi untuk kami, namun kami masih menggunakan jsdom 7 secara internal.

Saya akan menutup ini, karena saya percaya cara Object.defineProperty dalam melakukan sesuatu baik-baik saja. Jika itu tidak berhasil untuk Anda di jsdom 8, saya senang untuk membukanya kembali.

Keren, terima kasih saya akan mencoba ini.

@cpojer , sepertinya saya tidak tahu apa yang perlu saya klik untuk membuka kembali masalah ini ...

Apakah ada di lingkungan jest yang dapat dipanggil jsdom.changeUrl(window, url) seperti yang dijelaskan di sini https://github.com/tmpvar/jsdom#changing -the-url-of-an-existing-jsdom- jendela-instance di [email protected] ?

tiket lama tetapi bagi mereka yang masih mengalami masalah ini, kami telah mulai menggunakan window.location.assign() sebagai gantinya sehingga dalam pengujian kami, kami dapat mengejek fungsi assign seperti itu..

it('will redirect with a bad route', () => {
    window.location.assign = jest.fn();
    const redirection = shallow(<Redirection />, {
      context: {
        router: {
          location: {
            pathname: '/wubbalubbadubdub',
          },
        },
      },
    });
    expect(window.location.assign).toBeCalledWith(`${CONFIG.APP_LEGACY_ROOT}`);
  });

Terima kasih @th3fallen . Itu keren!

Btw @cpojer saya mulai di FB pada 1 Mei.... ;P

Bagus!

Saya mencoba memigrasi pengujian kami dari Mocha+Chai+Sinon.js ke Jest dan tidak dapat menemukan cara mengubah lokasi untuk pengujian tertentu.
Jest 19.x menggunakan JSDom 9.12 yang tidak memungkinkan untuk mengubah lokasi menggunakan trik Object.defineProperty . Juga, saya tidak dapat menggunakan jsdom.changeURL() karena alasan yang dijelaskan di tmpvar/jsdom#1700.
@cpojer bagaimana dengan menerapkan beberapa metode proxy ke jsdom.changeURL() di Jest?

@okovpashko kami berencana untuk mengekspos jsdom ke lingkungan: https://github.com/facebook/jest/issues/2460

Object.defineProperty bekerja untuk kami di FB.

@thymikee Saya melihat masalah itu tetapi berpikir bahwa proposisi itu ditolak.
@cpojer Saya salah membaca contoh Anda dan mencampurnya dengan yang lain terkait dengan masalah ini, di mana orang menyarankan untuk menggunakan Object.defineProperty(window, 'location', {value: 'url'}); . Terima kasih!

Saya perlu mengubah tidak hanya href, jadi saya menulis metode sederhana, yang mungkin berguna bagi seseorang yang akan membaca utas ini:

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

Maaf karena menyeret utas ini lebih jauh, tetapi saya telah mencoba mengejek fungsi Push seperti yang disarankan ...

reactRouterReduxMock.push = (url) => {
   Object.defineProperty(window.location, 'href', {
    writable: true,
    value: url
       })
})

tapi saya masih mendapatkan kesalahan jsdom yang sepertinya tidak bisa saya selesaikan:

       TypeError: Cannot read property '_location' of null
       at Window.location (/Users/user/projects/app/client/node_modules/jsdom/lib/jsdom/browser/Window.js:148:79)
       at value (/Users/user/projects/app/client/test/integration-tests/initialSetup.js:122:32) //this is the defineProperty line above

Saya menyadari ini adalah kesalahan jsdom, tetapi bagi mereka yang telah memecahkan ini, apakah ada lagi konteks pengaturan yang dapat Anda bagikan yang mungkin membuat saya menyelesaikan ini?

Terima kasih

@matt-dalton coba saran saya di https://github.com/facebook/jest/issues/890#issuecomment -295939071 berfungsi dengan baik untuk saya

@matt-dalton apa URL Anda? apakah Anda memiliki testURL yang disetel di jest-config.json Anda atau apakah itu diinisialisasi sebagai about:blank ?

@ianlyons Ya saya menetapkan nilai "https://test.com/" untuk ini di package.json, dan tidak ada jalur yang muncul sebagai blank .

@ th3fallen Jika saya memahami Anda dengan benar, saya rasa ini tidak berfungsi untuk kasus penggunaan saya. Apakah Anda meneruskan url sebagai nilai konteks yang menyebabkan penetapan dipicu? Saya mencoba menyusun tes integrasi yang belum sempurna, jadi saya ingin memeriksa bagaimana router merespons beban data awal. Saya telah mengolok-olok respons API, dan kemudian membutuhkan perubahan URL untuk dilakukan menggunakan logika aplikasi (yaitu saya sendiri tidak ingin memicunya secara eksternal).

Object.defineProperty tampaknya melakukan trik untuk menguji fungsionalitas yang bergantung pada window.location.search , misalnya. Dikatakan bahwa itu bermutasi window.location.search sehingga tes lain mungkin terpengaruh. Apakah ada cara untuk "membatalkan" perubahan yang Anda buat pada window.location.search melalui Object.defineProperty , seperti fungsi lelucon yang memiliki fungsi mockReset ?

@msholty-fd Anda dapat mencoba pendekatan ini:

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

Itu berhenti bekerja di Jest 22.0.1

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

Pesan eror:

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

Hmm, entah bagaimana kita mungkin perlu mengizinkan orang untuk menelepon reconfigure . https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-reconfiguresettings

Membuka masalah baru yang terkait dengan ini, karena yang ini sudah ditutup: #5124

@SimenB Saya tidak yakin bahwa Jest harus memperbaiki ini. JSDOM harus mengizinkan window.location.assign() untuk bekerja sebagaimana dimaksud dan mengkonfigurasi ulang output dari window.location.href dll.

Saya mendapat TypeError: Could not parse "/upgrades/userlogin?hardwareSku=sku1351000490stgvha" as a URL karena jsdom memiliki url dasar default ke about:blank

Saya mencoba menetapkan url dasar ke jsdom , menghabiskan 4 jam tanpa berhasil (saya tahu cara melakukannya, cukup masukkan <base href='your_base_url' /> ke dom; tetapi , dom dibuat oleh jest , bukan oleh saya, jadi saya menyerah.

solusi Object.defineProperty hanya berfungsi dengan versi lama jsdom (Anda mendapatkan 'tidak dapat mendefinisikan ulang kesalahan properti dengan versi jsdom yang lebih baru);
jika Anda menggunakan jsdom ver > 10, seperti yang disebutkan @th3fallen adalah solusi yang tepat.
gunakan window.location.assign adalah cara yang tepat

Jika Anda hanya menginginkan url selain about:blank , Anda dapat menggunakan konfigurasi testURL .

terima kasih @SimenB atas balasan Anda.

Tidak, saya berbicara tentang base url bukan url . Saya memiliki kode yang akan melakukan window.location.href="/login" dan ketika menjalankan jest , jsdom membuang pengecualian mengeluh /login bukan url yang valid

TypeError: Could not parse "/login" as a URL

Saya memeriksa kode sumber jsdom dan menyadari ini karena saya tidak memiliki pengaturan url dasar (ini setara dengan mengetik "/ login" di bilah URL browser tanpa alamat dasar).

dengan jsdom , biasanya kita dapat mengatur url dasar melalui

global.jsdom = new JSDOM('<html><head> <base href="base_url" /></head></html>')

tetapi karena jest mengatur jsdom , itu di luar kendali kami.
--- pembaruan: Saya kira saya dapat secara eksplisit menambahkan jsdom sebagai ketergantungan dan mengkonfigurasi jsdom secara manual.

Saya kemudian menemukan solusi yaitu mengganti window.location.href= dengan window.location.assign dan mengejek fungsi assign dan itu berhasil untuk saya

@bochen2014 masalah ini memiliki informasi lebih lanjut tentang cara menggunakan versi jsdom yang lebih baru: #5124

tl;dr: Anda dapat mengejek window.location.assign() , atau Anda dapat menggunakan jest-environment-jsdom-global , yang memungkinkan Anda mengonfigurasi ulang jsdom dalam penerbangan.

terima kasih @simon360

itulah yang saya lakukan ;-)
Saya menggunakan jsdom.reconfigure untuk menyiapkan urls awal yang berbeda dalam pengujian saya, dan setiap kali saya perlu mengubah url dalam kode (bukan pengujian), saya menggunakan window.location.assign dan mengejeknya. yang bekerja untuk saya.

hanya untuk orang-orang yang mungkin/akan mengalami masalah yang sama, untuk mengatur url untuk jsdom Anda

// jest.config.js
 module.exorts={ 
  testURL: 'http://localhost:3000',
  // or : 
  testEnvironmentOptions: {
     url: "http://localhost:3000/",
    referrer: "https://example.com/",
  }
}

perhatikan bahwa ini akan mengatur url untuk semua pengujian Anda;
jika Anda menginginkan url yang berbeda dalam beberapa pengujian tertentu, gunakan jsdom.reconfigure api;
jika Anda perlu mengubah url dengan cepat di luar kode unit test (yaitu kode produksi), Anda perlu menggunakan window.location.assign dan mengejeknya.

Diposting di tiket lain, tapi saya posting di sini:

Menemukan solusi yang bagus untuk Jest 21.2.1

Ok, sejauh ini solusi termudah untuk mengatasi ini adalah:
Masuk ke pengaturan Jest Anda (misalnya saya akan menggunakan package.json):

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

Sekarang Anda akan memiliki akses ke objek jendela 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.

@petar-prog91 yang sangat membantu. Anda memiliki kesalahan ketik - seharusnya testURL bukan TestURL

@BarthesSimpson terima kasih atas pemberitahuannya, komentar yang diperbarui.

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

Hai,
Saya telah menggunakan ini dalam pengujian, saya menghapus status global dan membuat yang baru dengan jsdom... :

   describe('componentDidMount', () => {
    delete global.window
    const window = (new JSDOM(``, {url: 'https://example.org/'})).window
    global.window = window
    describe('When window is defined', () => {
      const spy = jest.spyOn(Utils, 'extractTokenFromUrl')
      it('should call extract token function with window location', () => {
        mount(<Header />)
        expect(spy).toHaveBeenCalledWith('https://example.org/')
      })
    })
  })

@UserNT konfirmasi — itu memberikan TypeError: Cannot redefine property: href

@annemarie35 tidak berfungsi — ReferenceError: JSDOM is not defined

Saya tidak tahu apakah ini akan membantu seseorang, tetapi inilah yang saya lakukan saat ini.

const redirectTo = (url: string): void => {
  if (process.env.NODE_ENV === "test") {
    global.jsdom.reconfigure({ url: `${getBaseUrl()}${url}` });
  } else {
    window.location.replace(url);
  }
};

Tulis fungsi redirect dan gunakan itu sebagai gantinya. Jadi dalam pengujian env, itu akan bergantung pada url jsdom.reconfigure untuk mengubah bagian url.

Saya menggunakannya seperti ini

export const clientFetchData = (
  history: Object,
  routes: Object,
  store: Object
) => {
  const callback = location =>
    match({ routes, location }, (error, redirectLocation, renderProps) => {
      if (error) {
        redirectTo("/500.html");
      } else if (redirectLocation) {
        redirectTo(redirectLocation.pathname + redirectLocation.search);
      } else if (renderProps) {
        if (!isEmpty(window.prerenderData)) {
          // Delete initial data so that subsequent data fetches can occur
          window.prerenderData = undefined;
        } else {
          // Fetch mandatory data dependencies for 2nd route change onwards
          trigger(
            FETCH_DATA_HOOK,
            renderProps.components,
            getDefaultParams(store, renderProps)
          );
        }

        trigger(
          UPDATE_HEADER_HOOK,
          renderProps.components,
          getDefaultParams(store, renderProps)
        );
      } else {
        redirectTo("/404.html");
      }
    });

  history.listen(callback);
  callback(history.getCurrentLocation());
};

Setelah itu, dalam pengujian Anda, bisa seperti ini

    describe("# match route", () => {
      it("should navigate to error page", () => {
        fetchData.clientFetchData(history, components, store);
        reactRouter.match.mock.calls[0][1](true);
        expect(window.location.href).toEqual(`${SERVER_URL}/500.html`);
      });

      it("should redirect to /hello-world.html page", () => {
        fetchData.clientFetchData(history, components, store);
        reactRouter.match.mock.calls[0][1](undefined, {
          pathname: "/hello-world.html",
          search: ""
        });
        expect(window.location.href).toEqual(`${SERVER_URL}/hello-world.html`);
      });
...

Saya akhirnya melakukan ini yang berhasil:

global.window = new jsdom.JSDOM('', {
  url: 'http://www.test.com/test?foo=1&bar=2&fizz=3'
}).window;

Saya memiliki ini di bagian atas file pengaturan JSDOM saya:

const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
  url: "http://test.com"
});
const { window } = jsdom;

function copyProps(src, target) {
  const props = Object.getOwnPropertyNames(src)
    .filter(prop => typeof target[prop] === 'undefined')
    .map(prop => Object.getOwnPropertyDescriptor(src, prop));
  Object.defineProperties(target, props);
}

global.document = window.document;
global.window = window;
global.navigator = {
  userAgent: 'node.js',
};

global.HTMLElement = window.HTMLElement;

Memperbaikinya dengan mengatur "testURL": " http://localhost/ " di Jest config (Saya menggunakan versi terbaru). Secara default " about:blank " dan itu menyebabkan kesalahan JSDOM (Anda tidak dapat mengubah url "about:blank" ke yang lain).

Sumber daya:
http://jestjs.io/docs/en/configuration#testurl -string
https://github.com/jsdom/jsdom/issues/1372

Saya menemukan posting ini sangat membantu: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"Dalam konfigurasi Jest Anda, pastikan untuk mengatur yang berikut:

"testURL": "https://www.somthing.com/test.html"

Kemudian di bagian beforeEach() Anda untuk pengujian Anda, ubah jalur sesuai kebutuhan dengan menggunakan
sejarah.pushState().

window.history.pushState({}, 'Test Title', '/test.html?query=true');

Voila! Sekarang Anda mengubah jalur Anda untuk pengujian apa pun, tanpa harus mengganti konfigurasi jsdom apa pun seperti yang disarankan orang lain di utas yang disebutkan di atas. Tidak yakin di utas mana saya menemukan solusi ini, tetapi pujian untuk dev yang mempostingnya!"

@Mike-Tran Anda keren! Itu benar-benar berhasil, sangat sederhana. Saya bahkan tidak perlu menggunakan pengaturan testURL.

@Mike-Tran Itu berhasil! Terimakasih! Namun, saya tidak membutuhkan testURL atau beforeEach . Saya baru saja melakukannya:

window.history.pushState({}, 'Test Title', '/test.html?query=true');

Dan sekarang saya tidak perlu menggunakan Object.defineProperty lagi

@jcmcneal terima kasih yang melakukannya untuk saya! (bercanda 23.0.0)

Jika tujuan Anda adalah untuk mengejek objek window , inilah solusi saya (tidak begitu elegan, tetapi berhasil):

Buat kelas antarmuka (tidak yakin apakah antarmuka adalah kata yang tepat, tapi saya harap Anda mengerti maksudnya):

// window.js
export const { location } = window;

Dalam kode Anda yang sebenarnya, tukar window dengan metode antarmuka, misalnya win

// myFile.js
import * as win from './window';

export function doSomethingThatRedirectsPage() {
  win.location.href = 'google.com';
}

Kemudian, dalam tes lelucon Anda, Anda hanya mengejeknya sehingga jsdom tidak mengeluh. Anda bahkan dapat menegaskannya:

// myFile.test.js
import * as myFile from './myFile';
import * as win from './window';

it('should redirect', () => {
  win.location = { href: 'original-url' };

  expect(win.location.href).toBe('original-url');

  myFile.doSomethingThatRedirectsPage();

  expect(win.location.href).toBe('google.com');
});

@Mike-Tran , @jcmcneal terima kasih! Semua bekerja sebagai aspek!

class SSOtestComponent memperluas React.Component {

componentDidMount() {
    let isSuccess = this.props.location.pathname === '/sso/test/success' ? true : false

    window.opener.postMessage({ type: "sso_test", isSuccess,...this.props.location.query}, window.location.origin)
}

onSsoAuthenticate() {

}

componentWillUnmount() {
}

render() {
    return (<Loader />);
}

}

module.exports = SSOtestComponent;

saya menulis unit test case menggunakan enjyme dan bercanda bagaimana menulis kondisi window.location ... tolong beri jawabannya

ini berhasil untuk saya

    const location = JSON.stringify(window.location);
    delete window.location;

    Object.defineProperty(window, 'location', {
      value: JSON.parse(location)
    });

    Object.defineProperty(global.location, 'href', {
      value: 'http://localhost/newURL',
      configurable: true
    });

pada versi lelucon 23.6.0

Ini berhasil untuk saya.

delete global.window.location
global.window.location = { href: 'https://test-domain.com.br', ...anyOptions }

@FelipeBohnertPaetzold terima kasih

Terima kasih @FelipeBohnertPaetzold. Saya menggunakan location.host dalam kode saya, jadi ternyata saya membutuhkan objek lokasi lengkap , jadi yang berikut ini berfungsi lebih baik untuk saya, daripada harus secara manual melewati setiap properti lokasi:

delete global.window.location;
global.window.location = new URL("https://www.ediblecode.com/");

Catatan, ini berfungsi di Node 6.13+ (lihat URL class docs ) dan saya menggunakan Jest 24.

Perhatikan juga, ini tidak berfungsi dengan URL relatif, lihat https://url.spec.whatwg.org/#example -url-parsing.

TypeScript ini berfungsi untuk saya di Jest 24.0.0 dan Node 10.15.0 :

src/setupTests.ts

import { mockWindow } from './testUtils';
mockWindow(window, 'http://localhost');

src/setupTests.test.ts

describe('setup tests', () => {

    describe('window.location', () => {
        const saveLocation = window.location;

        afterAll(() => {
            delete window.location;
            window.location = saveLocation;
        });

        it('location.assign assigns a location', () => {
            window.location.assign('http://foo.com');

            expect(window.location.href).toBe('http://foo.com/');

            (window.location.assign as jest.Mock<void, [string]>).mockClear();
        });

        it('location.replace replaces a location', () => {
            window.location.replace('http://bar.com');

            expect(window.location.href).toBe('http://bar.com/');

            (window.location.replace as jest.Mock<void, [string]>).mockClear();
        });

        it('location.reload is a spy', () => {
            window.location.reload();

            expect(window.location.reload).toHaveBeenCalledTimes(1);

            (window.location.reload as jest.Mock).mockClear();
        });
    });
});

src/testUtils.ts

interface MockedLocation extends Location {
    assign: jest.Mock<void, [string]>;
    reload: jest.Mock;
    replace: jest.Mock<void, [string]>;
}

interface MockedWindow extends Window {
    location: MockedLocation;
}

export function mockWindow(win: Window = window, href = win.location.href) {
    const locationMocks: Partial<MockedLocation> = {
        assign: jest.fn().mockImplementation(replaceLocation),
        reload: jest.fn(),
        replace: jest.fn().mockImplementation(replaceLocation),
    };

    return replaceLocation(href);

    function replaceLocation(url: string) {
        delete win.location;
        // tslint:disable-next-line:no-any
        win.location = Object.assign(new URL(url), locationMocks) as any;
        return win as MockedWindow;
    }
}

src/testUtils.test.ts

import { mockWindow } from './testUtils';

describe('test utils', () => {

    describe('mockWindow', () => {
        const saveLocation = window.location;

        afterAll(() => {
            delete window.location;
            window.location = saveLocation;
        });

        it('location.assign assigns a location', () => {
            const { assign } = mockWindow().location;
            assign('http://foo.com');

            expect(window.location.href).toBe('http://foo.com/');

            assign.mockClear();
        });

        it('location.replace replaces a location', () => {
            const { replace } = mockWindow().location;
            replace('http://bar.com');

            expect(window.location.href).toBe('http://bar.com/');

            replace.mockClear();
        });

        it('location.reload is a spy', () => {
            const { reload } = mockWindow().location;
            reload();

            expect(window.location.reload).toHaveBeenCalledTimes(1);

            reload.mockClear();
        });
    });
});

@jedmao

Hei, man) Utilitas yang bagus!

Bagi saya pengujian di src/setupTests.test.ts sedikit berlebihan, karena Anda sudah sepenuhnya menguji util mockWindow di src/testUtils.test.ts. Jadi, dalam pengujian untuk src/setupTests.ts sudah cukup untuk menguji, bahwa Anda memanggil mockWindow dengan argumen yang benar.

Terima kasih)

@tzvipm @jup-iter terima kasih atas 👍. Saya baru saja merilis @jedmao/storage dan @jedmao/location , yang keduanya sepenuhnya agnostik dari Jest. Anda seharusnya dapat spyOn metode yang sesuai tanpa menulis tes tambahan apa pun, karena paket npm telah diuji sepenuhnya.

Jika Anda mendapatkan kesalahan ini saat menggunakan Vue, cukup gunakan this.$router.push({...}) daripada this.$router.go({...})

image

Tempatkan kode di bawah ini pada baris 1:
delete global.window.location;
global.window.location = "";

Peristiwa klik yang mengubah window.location sekarang dapat ditangkap.

"bercanda": "^23.6.0"
v10.15.0
6.5.0

Ini bekerja:

delete window.location;
window.location = Object.assign({}, window.location);
const url = Object.assign({}, new URL('http://google.com'));
Object.keys(url).forEach(prop => (window.location[prop] = url[prop]));

Atau lebih baik lagi...

delete (global as any).window;
(global as any).window = new JSDOM(undefined, { url: 'http://google.com' }).window;

Anda benar, ini memang masalah jsdom. Di Facebook, apa yang telah kami lakukan untuk mengatasinya, adalah menggunakan ini:

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

ini berfungsi untuk kami, namun kami masih menggunakan jsdom 7 secara internal.

Saya akan menutup ini, karena saya percaya cara Object.defineProperty dalam melakukan sesuatu baik-baik saja. Jika itu tidak berhasil untuk Anda di jsdom 8, saya senang untuk membukanya kembali.

Ya, saya memiliki beberapa fungsi yang berurusan dengan location.search dan location.hash , dan saya ingin mengujinya dengan defineProperty seperti yang Anda sebutkan. Ini tidak akan berhasil!

Ketika saya mematikan mode diam jest , saya menemukan ini: Error: Not implemented: navigation (except hash changes)

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
    Error: Not implemented: navigation (except hash changes)
        at module.exports (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
        at navigateFetch (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:74:3)
        at exports.navigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:52:3)
        at LocationImpl._locationObjectNavigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:29:5)
        at LocationImpl.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:213:10)
        at Location.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/generated/Location.js:93:25)
        at Object.assign (/home/ghlandy/projects/wdph-utils/src/__tests__/url.test.js:6:14)
        at Object.asyncJestLifecycle (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
        at resolve (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>) undefined

Dan sekarang saya tidak tahu bagaimana cara menguji fungsi saya.

Adakah yang punya cara untuk mengubah tes url

Anda benar, ini memang masalah jsdom. Di Facebook, apa yang telah kami lakukan untuk mengatasinya, adalah menggunakan ini:

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

ini berfungsi untuk kami, namun kami masih menggunakan jsdom 7 secara internal.
Saya akan menutup ini, karena saya percaya cara Object.defineProperty dalam melakukan sesuatu baik-baik saja. Jika itu tidak berhasil untuk Anda di jsdom 8, saya senang untuk membukanya kembali.

Ya, saya memiliki beberapa fungsi yang berurusan dengan location.search dan location.hash , dan saya ingin mengujinya dengan defineProperty seperti yang Anda sebutkan. Ini tidak akan berhasil!

Ketika saya mematikan mode diam jest , saya menemukan ini: Error: Not implemented: navigation (except hash changes)

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
    Error: Not implemented: navigation (except hash changes)
        at module.exports (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
        at navigateFetch (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:74:3)
        at exports.navigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/navigation.js:52:3)
        at LocationImpl._locationObjectNavigate (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:29:5)
        at LocationImpl.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/window/Location-impl.js:213:10)
        at Location.assign (/home/ghlandy/projects/wdph-utils/node_modules/jsdom/lib/jsdom/living/generated/Location.js:93:25)
        at Object.assign (/home/ghlandy/projects/wdph-utils/src/__tests__/url.test.js:6:14)
        at Object.asyncJestLifecycle (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
        at resolve (/home/ghlandy/projects/wdph-utils/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>) undefined

Dan sekarang saya tidak tahu bagaimana cara menguji fungsi saya.

Adakah yang punya cara untuk mengubah tes url

Dan dalam situasi saya, memiliki bidang testURL dalam file jest.config.js dapat berfungsi. Tetapi bagaimana jika saya ingin mengubah testURL sebelum setiap pengujian.

Saya menemukan posting ini sangat membantu: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"Dalam konfigurasi Jest Anda, pastikan untuk mengatur yang berikut:

"testURL": "https://www.somthing.com/test.html"

Kemudian di bagian beforeEach() Anda untuk pengujian Anda, ubah jalur sesuai kebutuhan dengan menggunakan
sejarah.pushState().

window.history.pushState({}, 'Test Title', '/test.html?query=true');

Voila! Sekarang Anda mengubah jalur Anda untuk pengujian apa pun, tanpa harus mengganti konfigurasi jsdom apa pun seperti yang disarankan orang lain di utas yang disebutkan di atas. Tidak yakin di utas mana saya menemukan solusi ini, tetapi pujian untuk dev yang mempostingnya!"


Solusi luar biasa!!! Terima kasih banyak! @Mike-Tran
Saya menginginkan solusi singkat dan tidak invasif seperti ini!

Agar ini berfungsi pada Juni 2019 saya harus melakukan ini:

    delete global.window.location;
    global.window = Object.create(window);
    global.window.location = {
      port: '123',
      protocol: 'http:',
      hostname: 'localhost',
    };

saya pakai ini....

window.history.pushState({}, '', `${url}/`);

Mungkin bagian dari JSDOMTestWrapper saya dapat membantu seseorang

    /** <strong i="6">@type</strong> {Window} */
    this.testWindowObject = Object.create(window);
    const wnd = this.testWindowObject;
    this.testWindowObject.history = {
      state: null,
      prev: { /** <strong i="7">@todo</strong> 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);
    }

Pendekatan ini berfungsi pada 27 Sep 2019: https://stackoverflow.com/a/54034379/1344144

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
    value: {
       href: url
    },
    writable: true
});

Solusi lain bekerja untuk saya saat ini tanpa menulis jsdom :

  1. Pastikan Anda telah menyetel testURL di jest.config.js berapa pun nilainya:
// jest.config.js
'testURL': 'https://someurl.com'

Dalam file pengujian Anda:

window.history.pushState({}, 'Mocked page title', 'www.yoururl.com');

Dipetik dari: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking. Terima kasih untuk Ryan!

tidak bekerja untuk saya:

TypeError: Penetapan ke properti hanya-baca tidak diizinkan dalam mode ketat

image

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

tidak bekerja untuk saya:

TypeError: Penetapan ke properti hanya-baca tidak diizinkan dalam mode ketat

image

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

tambahkan ini ke file global.

hapus global.window.location;
global.window.location = "";

Pendekatan ini berfungsi pada 27 Sep 2019: https://stackoverflow.com/a/54034379/1344144

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
    value: {
       href: url
    },
    writable: true
});

Saya mencoba sesuatu yang serupa, dengan location.assign, tetapi sepertinya ini tidak berfungsi lagi.

ini bekerja untuk saya di lelucon 24.9.0

 window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');

ini bekerja untuk saya di lelucon 24.9.0

 window.history.replaceState({}, 'Test Title', '/test?userName=James&userNumber=007');

Saya harus membuat kode async agar ini berfungsi karena saya menjalankan kode di dalam janji.

jadi bekerja sekarang

Bagaimana cara menguji lokasi chenge di tindakan vuex?

async setForm({ rootState, state, commit, dispatch }, formData) {
          ....
          switch (answ.result.type) {
            ....
            case 'redirect':
              console.log(answ.data.url);
              window.location = answ.data.url;
              console.log({ location: window.location.href });
              break;
            default:
              break;
it('setForm - success, redirect', async done => {
      expect(window.location.href).toBe('https://www.google.ru/');

Saya memiliki kesalahan:

    expect(received).toBe(expected) // Object.is equality

    Expected: "https://www.google.ru/"
    Received: "http://localhost/"

ini berhasil untuk saya

    const location = JSON.stringify(window.location);
    delete window.location;

    Object.defineProperty(window, 'location', {
      value: JSON.parse(location)
    });

    Object.defineProperty(global.location, 'href', {
      value: 'http://localhost/newURL',
      configurable: true
    });

pada versi lelucon 23.6.0

apa globalnya?

di mana definisi globalnya?

Ini berhasil untuk saya.

delete global.window.location
global.window.location = { href: 'https://test-domain.com.br', ...anyOptions }

ini membuat Lokasi dengan semua fungsi asli, tetapi dapat diolok-olok:

beforeAll(() => {
  const location = window.location
  delete global.window.location
  global.window.location = Object.assign({}, location)
})
Apakah halaman ini membantu?
0 / 5 - 0 peringkat