ChatGPT解决这个技术问题 Extra ChatGPT

如何用 Jest + Vuejs 模拟 window.location.href?

目前,我正在为我的项目实施单元测试,并且有一个包含 window.location.href 的文件。

我想模拟这个来测试,这是我的示例代码:

it("method A should work correctly", () => {
      const url = "http://dummy.com";
      Object.defineProperty(window.location, "href", {
        value: url,
        writable: true
      });
      const data = {
        id: "123",
        name: null
      };
      window.location.href = url;
      wrapper.vm.methodA(data);
      expect(window.location.href).toEqual(url);
    });

但我得到这个错误:

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

我尝试了一些解决方案,但没有解决。我需要一些提示来帮助我摆脱这个麻烦。请帮忙。

这已在另一个线程 Here is the link to the answer 中得到解答
不完全是您要查找的内容,但 window.location.assign(url) 在功能上做了同样的事情,因此您可以使用 jest.spyOn(window.location, 'assign').mockImplementation(() => {}); 来模拟它

m
mava

你可以试试:

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

查看该问题的 Jest Issue:
Jest Issue


对我来说,使用 global 不起作用,我删除了 global.,我还需要添加 writable: true,否则一旦设置其他测试就无法更改它。
j
jabacchetta

2020 更新

基本的

URL object 具有许多与 Location object 相同的功能。换句话说,它包括诸如 pathnamesearchhostname 等属性。因此对于大多数情况,您可以执行以下操作:

delete window.location
window.location = new URL('https://www.example.com')

先进的

您还可以模拟您可能需要的 Location methods,它在 URL 界面上不存在:

const location = new URL('https://www.example.com')
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()

delete window.location
window.location = location

使用 typescript 更新,delete window.location 将触发错误 The operand of a 'delete' operator must be optional
@dhsun 对您上面提到的问题有任何解决方案吗?我面临类似的问题
添加 // @ts-ignore 删除 window.location;如果你必须
Reflect.deleteProperty(global.window, 'location') 处理没有 ts 错误
要解决该 TS 错误,您可以执行 delete (window as any).location;
T
Tran Son Hoang

我已通过添加 writable: true 并将其移至 beforeEach 解决了此问题

这是我的示例代码:

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

此解决方案也适用于在 Jest 测试中覆盖 window.location.hostname。我需要 writable: true 才能多次更改 hostname
这对我有很大帮助
这就是我一直在寻找的。对我来说最好的解决方案。
s
serv-inc

2019 from GitHub 的解决方案:

删除 global.window.location; global.window = Object.create(window); global.window.location = { 端口:'123',协议:'http:',主机名:'localhost',};


这是唯一一个似乎对我有用的,并且有一个有用的理由! :)
它可以工作,但使用需要原始 location.host 属性的 postMessage 会破坏以下测试
K
Kaiido

最好的办法可能是创建一个新的 URL 实例,以便它像 location.href 那样解析您的字符串,并更新 location 的所有属性,如 .hash.search.protocol 等。

it("method A should work correctly", () => {
  const url = "http://dummy.com/";
  Object.defineProperty(window, "location", {
    value: new URL(url)
  } );

  window.location.href = url;
  expect(window.location.href).toEqual(url);

  window.location.href += "#bar"
  expect(window.location.hash).toEqual("#bar");
});

https://repl.it/repls/VoluminousHauntingFunctions


J
Juan Lago

提供的许多示例都没有模拟原始 Location 对象的属性。

我所做的只是用 URL 替换 Location 对象(window.location),因为 URL 包含与 Location 对象相同的属性,例如“href”、“search”、“hash”、“host”。

Setter 和 Getter 的工作方式也与 Location 对象完全一样。

例子:

const realLocation = window.location;

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

    afterEach(() => {
        window.location = realLocation;
    });

    test('My test func', () => {

        // @ts-ignore
        delete window.location;

        // @ts-ignore
        window.location = new URL('http://google.com');

        console.log(window.location.href);

        // ...
    });
});

我想知道它是否不应该是 const realLocation = Object.assign({}, window.location); 因为我觉得直接分配它会传递一个稍后被覆盖的引用。想法?
原始 window.location 指向的对象在此代码中没有发生变化—— window.location 属性在其中设置了不同的对象。由于原始窗口对象没有突变,因此无需克隆它。
D
Devin Clark

2020 年 @testing-library/react 的 window.location.assign 工作示例:

  afterEach(cleanup)
  beforeEach(() => {
    Object.defineProperty(window, 'location', {
      writable: true,
      value: { assign: jest.fn() }
    })
  })

writable: true 是我的单元测试工作所必需的,否则后续测试将无法将其覆盖为其他内容。谢谢
t
thisismydesign

扩展@jabacchetta 的解决方案以避免此设置渗入其他测试:

describe("Example", () => {
  let location;

  beforeEach(() => {
    const url = "https://example.com";
    location = window.location;
    const mockLocation = new URL(url);
    mockLocation.replace = jest.fn();
    delete window.location;
    window.location = mockLocation;
  });

  afterEach(() => {
    window.location = location;
  });
});

l
lequan

你可以试试帮手:

const setURL = url => global.jsdom.reconfigure({url});

describe('Test current location', () => {
  test('with GET parameter', () => {
    setURL('https://test.com?foo=bar');
    // ...your test here
  });
});

E
Estevão Lucas

这对 Jest + TypeScript + Next.js 有效(如果您使用 useRoute().push

const oldWindowLocation = window.location;

beforeAll(() => {
  delete window.location;
  window.location = { ...oldWindowLocation, assign: jest.fn() };
});

afterAll(() => {
  window.location = oldWindowLocation;
});

E
Ed Lucas

JSDOM 版本

另一种方法,使用 JSDOM,它将提供 window.location.hrefwindow.location 的所有其他属性(例如 window.location.search 来获取查询字符串参数)。

import { JSDOM } from 'jsdom';

...

const { window } = new JSDOM('', {
    url: 'https://localhost/?testParam=true'
});
delete global.window;
global.window = Object.create(window);

T
Tyler Caine Rhodes

如何在代码库中重新分配 window.location;我们为 Jest 测试找到的最简单的工作设置:

const realLocation = window.location;

beforeEach(() => {
  delete window.location;
});

afterEach(() => {
  window.location = realLocation;
});

M
Muhammed Moussa

你可以试试jest-location-mock

npm install --save-dev jest-location-mock

更新 jest.config.js 文件中的 jest 配置或 package.json 内的 jest 道具:

setupFilesAfterEnv: [ "./config/jest-setup.js" ]

创建jest-setup.js

import "jest-location-mock";

用法:

it("should call assign with a relative url", () => {
    window.location.assign("/relative-url");
    expect(window.location).not.toBeAt("/");
    expect(window.location).toBeAt("/relative-url");
});

s
skanecode

可以通过在每次测试中删除这个全局来重写 window.location。

delete global.window.location;
const href = 'http://localhost:3000';
global.window.location = { href };

B
Bazze

根据上述示例和其他线程中的示例,以下是使用 jest 的具体示例,可能会对某人有所帮助:

describe('Location tests', () => {
    const originalLocation = window.location;

    const mockWindowLocation = (newLocation) => {
        delete window.location;
        window.location = newLocation;
    };

    const setLocation = (path) =>
        mockWindowLocation(
            new URL(`https://example.com${path}`)
        );

    afterEach(() => {
        // Restore window.location to not destroy other tests
        mockWindowLocation(originalLocation);
    });

    it('should mock window.location successfully', () => {
        setLocation('/private-path');

        expect(window.location.href).toEqual(
            `https://example.com/private-path`
        );
    });
});

I
Imran Khan

可能无关紧要。但是对于那些寻求 window.open('url', attribute) 解决方案的人来说,我在上面的一些评论的帮助下应用了这个:

window = Object.create(window);
const url = 'https://www.9gag.com';
Object.defineProperty(window, 'open', { value: url });

expect(window.open).toEqual(url);

S
Sgnl

这是一个简单的,您可以在每次测试中使用 beforeEach 或点菜。

它利用 Javascript window.history 及其 pushState 方法来操作 URL。

window.history.pushState({}, 'Enter Page Title Here', '/test-page.html?query=value');