Jest: window.location.href no se puede cambiar en las pruebas.

Creado en 13 abr. 2016  ·  70Comentarios  ·  Fuente: facebook/jest

Hola @cpojer ,

En realidad, esto es más un problema de jsdom@8 ... consulta tmpvar/jsdom#1388, pero también quiero anclar aquí para que Jest elija cualquier solución que se le ocurra a jsdom.

Anteriormente, con [email protected]/[email protected] podías escribir una prueba como esta:

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

Y esa prueba pasaría. Desde jsdom@8 esto ya no es posible y estas pruebas fallan.

Parece que jsdom está buscando algún tipo de capacidad, solo quería asegurarse de que Jest tomará esa capacidad cuando esté disponible.

Comentario más útil

Tienes razón, este es de hecho un problema de jsdom. En Facebook, lo que hemos hecho para solucionar esto es usar esto:

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

esto funciona para nosotros, sin embargo, todavía estamos en jsdom 7 internamente.

Cerraré esto, ya que creo que la forma Object.defineProperty de hacer las cosas está bien. Si eso no funciona para usted en jsdom 8, me complace volver a abrirlo.

Todos 70 comentarios

Tienes razón, este es de hecho un problema de jsdom. En Facebook, lo que hemos hecho para solucionar esto es usar esto:

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

esto funciona para nosotros, sin embargo, todavía estamos en jsdom 7 internamente.

Cerraré esto, ya que creo que la forma Object.defineProperty de hacer las cosas está bien. Si eso no funciona para usted en jsdom 8, me complace volver a abrirlo.

Genial, gracias voy a probar esto.

@cpojer , parece que no puedo averiguar en qué debo hacer clic para reabrir este problema...

¿Existe de todos modos en el entorno jest para que uno llame a jsdom.changeUrl(window, url) como se describe aquí https://github.com/tmpvar/jsdom# Changing -the-url-of-an-existing-jsdom- instancia de ventana en [email protected] ?

ticket antiguo, pero para aquellos que aún tienen este problema, comenzamos a usar window.location.assign() en su lugar, por lo que en nuestras pruebas podemos simular la función assign esta manera.

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

Gracias @th3fallen . ¡Eso es genial!

Por cierto @cpojer empiezo en FB el 1 de mayo... ;P

¡Bonito!

Estoy tratando de migrar nuestras pruebas de Mocha+Chai+Sinon.js a Jest y no sé cómo cambiar la ubicación de una prueba en particular.
Jest 19.x usa JSDom 9.12 que no permite cambiar la ubicación usando el truco Object.defineProperty . Además, no puedo usar jsdom.changeURL() por los motivos descritos en tmpvar/jsdom#1700.
@cpojer, ¿qué hay de implementar algún método proxy para jsdom.changeURL() en Jest?

@okovpashko estamos planeando exponer jsdom al medio ambiente: https://github.com/facebook/jest/issues/2460

Object.defineProperty nos funciona en FB.

@thymikee Vi ese problema pero pensé que la propuesta fue rechazada.
@cpojer Leí mal su ejemplo y lo mezclé con otros relacionados con este problema, donde las personas sugirieron usar Object.defineProperty(window, 'location', {value: 'url'}); . ¡Gracias!

Necesito cambiar no solo el href, así que escribí un método simple, que puede ser útil para alguien que lea este hilo:

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

Disculpas por alargar más este hilo, pero he intentado burlarme de la función push como se sugiere...

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

pero sigo recibiendo un error de jsdom que parece que no puedo evitar:

       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

Me doy cuenta de que se trata de un error de jsdom, pero para aquellos que lo han resuelto, ¿hay algún otro contexto de configuración que pueda compartir que me permita solucionarlo?

Gracias

@matt-dalton prueba mi sugerencia en https://github.com/facebook/jest/issues/890#issuecomment -295939071 funciona bien para mí

@matt-dalton ¿cuál es tu URL? ¿Tiene testURL establecido en su jest-config.json o se inicializa como about:blank ?

@ianlyons Sí, establecí un valor de "https://test.com/" para esto en el archivo package.json, y ninguna de las rutas se muestra como blank .

@th3fallen Si lo entiendo correctamente, no creo que esto funcione para mi caso de uso. ¿Está pasando la URL como un valor de contexto que hace que se active la asignación? Estoy tratando de armar una prueba de integración rudimentaria, así que quiero verificar cómo responde el enrutador a la carga de datos inicial. Me he burlado de la respuesta de la API y luego necesito que el cambio de URL se lleve a cabo utilizando la lógica de la aplicación (es decir, no quiero activarlo externamente yo mismo).

Object.defineProperty parece funcionar para probar la funcionalidad que se basa en window.location.search , por ejemplo. Dicho esto, muta window.location.search por lo que otras pruebas pueden verse afectadas. ¿Hay alguna manera de "deshacer" los cambios que ha realizado en window.location.search a través Object.defineProperty , como si las funciones simuladas de broma tuvieran la función mockReset ?

@ msholty-fd podría probar este enfoque:

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

Dejó de funcionar en Jest 22.0.1

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

Mensaje de error:

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

Abrió un nuevo tema relacionado con esto, ya que este estaba cerrado: #5124

@SimenB No estoy convencido de que Jest deba arreglar esto. JSDOM debería permitir que window.location.assign() funcione según lo previsto y reconfigure la salida de window.location.href etc.

Obtuve TypeError: Could not parse "/upgrades/userlogin?hardwareSku=sku1351000490stgvha" as a URL porque jsdom tiene la URL base predeterminada en about:blank

Traté de asignar una URL base a jsdom , pasé 4 horas sin éxito (sé cómo hacerlo, solo inserte <base href='your_base_url' /> en el dom; pero el dom es creado por jest , no por mí, así que me di por vencido.

la solución Object.defineProperty solo funciona con la versión anterior de jsdom (obtiene un error 'no se puede redefinir la propiedad con la versión posterior de jsdom );
si está usando jsdom ver > 10, como mencionó @th3fallen es la solución correcta.
usar window.location.assign es el camino correcto a seguir

Si solo desea otra URL que no sea about:blank , puede usar la configuración testURL .

gracias @SimenB por tu respuesta.

No, estaba hablando de base url no url . Tengo un código que hará window.location.href="/login" y cuando se ejecuta jest , jsdom lanza una excepción quejándose de que /login no es una URL válida

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

Revisé el código fuente de jsdom y me di cuenta de que esto se debe a que no tengo una configuración de URL base (esto es equivalente a escribir "/iniciar sesión" en la barra de URL del navegador sin una dirección base).

con jsdom , normalmente podemos configurar la URL base a través de

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

pero debido a que jest configuró jsdom , está fuera de nuestro control.
--- actualización: supongo que puedo agregar explícitamente jsdom como dependencia y configurar jsdom manualmente.

Luego encontré una solución que es sustituir window.location.href= con window.location.assign y simular la función assign y funcionó para mí

@ bochen2014 este número tiene más información sobre cómo usar la versión más nueva de jsdom: #5124

tl; dr: puede simular window.location.assign() , o puede usar jest-environment-jsdom-global , que le permitirá reconfigurar jsdom en vuelo.

gracias @simon360

eso fue lo que hice ;-)
Usé jsdom.reconfigure para configurar diferentes urls iniciales en mis pruebas, y cada vez que necesito cambiar la URL en el código (no en la prueba), uso window.location.assign y me burlo de él. que funcionó para mí.

solo para las personas que pueden / se encontrarán con el mismo problema, para configurar la URL para su jsdom

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

tenga en cuenta que esto establecerá la URL para todas sus pruebas;
si desea una URL diferente en algunas pruebas en particular, use jsdom.reconfigure api;
si necesita cambiar la URL sobre la marcha fuera del código de prueba de la unidad (es decir, el código de producción), debe usar window.location.assign y simularlo.

Lo publiqué en otro ticket, pero lo publicaré aquí:

Encontré una buena solución para Jest 21.2.1

Ok, hasta ahora la solución más fácil para esto es:
Vaya a la configuración de Jest (por ejemplo, usaré package.json):

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

Ahora tendrá acceso al objeto de la ventana y luego podrá configurar la URL a lo que quiera durante las pruebas.

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

Esperemos que esto ayude a alguien.

@ petar-prog91 que fue útil. Sin embargo, tiene un error tipográfico: debería ser testURL no TestURL

@BarthesSimpson gracias por el aviso, comentario actualizado.

Deja de publicar esto, no funciona en broma": "^22.4.2"

Hola,
He usado esto en la prueba, elimino el estado global y creo uno nuevo con 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 confirma: da TypeError: Cannot redefine property: href

@ annemarie35 no funciona — ReferenceError: JSDOM is not defined

No sé si esto podría ayudar a alguien, pero esto es lo que estoy haciendo actualmente.

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

Escribe una función de redirección y utilízala en su lugar. Entonces, al probar env, se basará en jsdom.reconfigure url para cambiar la parte de la URL.

lo uso asi

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

Después de eso, en tu prueba, puede ser algo así

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

Terminé haciendo esto que funcionó:

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

Tengo esto en la parte superior de mi archivo de configuración JSDOM:

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;

Se solucionó configurando "testURL": " http://localhost/ " en la configuración de Jest (estoy usando la última versión). De forma predeterminada, es " sobre: ​​en blanco " y estaba causando un error JSDOM (no puede cambiar la URL "sobre: ​​en blanco" a otra cosa).

Recursos:
http://jestjs.io/docs/en/configuration#testurl-cadena
https://github.com/jsdom/jsdom/issues/1372

Encontré esta publicación muy útil: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"En su configuración de Jest, asegúrese de configurar lo siguiente:

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

Luego, en su sección beforeEach () para su prueba, cambie la ruta según sea necesario usando
historia.pushState().

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

¡Voila! Ahora cambia su ruta para cualquier prueba, sin tener que anular ninguna configuración de jsdom como otros sugieren en el hilo mencionado anteriormente. No estoy seguro de en qué hilo encontré esta solución, ¡pero felicitaciones al desarrollador que la publicó!"

@Mike-Tran ¡Eres genial! Eso funcionó totalmente, así de simple. Ni siquiera tuve que usar la configuración testURL.

@Mike-Tran ¡Eso funciona! ¡Gracias! Sin embargo, no necesitaba testURL o beforeEach . Lo acabo de hacer:

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

Y ahora ya no tengo que usar Object.defineProperty 😅

@jcmcneal ¡ gracias que lo hizo por mí! (broma 23.0.0)

Si su objetivo es burlarse del objeto window , esta es mi solución (no tan elegante, pero funciona):

Cree una clase de interfaz (no estoy seguro si interfaz es la palabra correcta, pero espero que entienda el punto):

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

En su código real, intercambie window con el método de interfaz, por ejemplo, win

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

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

Luego, en tus pruebas de broma, simplemente te burlas de ellas para que jsdom no se queje. Incluso puedes afirmarlos:

// 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 , ¡gracias! ¡Todo funciona como aspectado!

clase SSOtestComponent extiende 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 />);
}

}

módulo.exportaciones = SSOtestComponent;

estoy escribiendo el caso de prueba de la unidad usando enjyme y bromeando cómo escribiré la ventana de condición. ubicación ... por favor dé la respuesta

esto funcionó para mí

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

en broma versión 23.6.0

Esto funcionó para mí.

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

@FelipeBohnertPaetzold gracias

Gracias @FelipeBohnertPaetzold. Estaba usando location.host en mi código, así que descubrí que necesitaba un objeto de ubicación completo , por lo que lo siguiente funcionó mejor para mí, en lugar de tener que pasar manualmente cada propiedad de ubicación:

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

Tenga en cuenta que esto funciona en Node 6.13+ (consulte los documentos de clase de URL ) y estaba usando Jest 24.

También tenga en cuenta que esto no funciona con URL relativas, consulte https://url.spec.whatwg.org/#example -url-parsing.

Este TypeScript me funciona en Jest 24.0.0 y 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.prueba.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

Oye, hombre) ¡Gran utilidad!

Para mí, las pruebas en src/setupTests.test.ts son un poco redundantes, porque ya probó completamente la utilidad mockWindow en src/testUtils.test.ts. Entonces, en las pruebas para src/setupTests.ts es suficiente para probar que llame a mockWindow con los argumentos correctos.

Gracias)

@tzvipm @jup-iter gracias por el 👍. Acabo de lanzar @jedmao/storage y @jedmao/location , que son completamente independientes de Jest. Debería poder spyOn los métodos apropiados sin escribir ninguna prueba adicional, ya que los paquetes npm vienen completamente probados.

En caso de que reciba este error al usar Vue, simplemente use this.$router.push({...}) en lugar de this.$router.go({...})

image

Coloque el siguiente código en la línea 1:
delete global.window.location;
global.window.location = "";

Ahora se puede capturar un evento de clic que está cambiando la ubicación de la ventana.

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

Esto funciona:

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

O mejor aún...

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

Tienes razón, este es de hecho un problema de jsdom. En Facebook, lo que hemos hecho para solucionar esto es usar esto:

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

esto funciona para nosotros, sin embargo, todavía estamos en jsdom 7 internamente.

Cerraré esto, ya que creo que la forma Object.defineProperty de hacer las cosas está bien. Si eso no funciona para usted en jsdom 8, me complace volver a abrirlo.

Sí, tengo algunas funciones que se ocupan de location.search y location.hash , y quiero probarlo con defineProperty como mencionaste. ¡No funcionará!

Cuando apago el modo silencioso jest , encontré esto: 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

Y ahora no tengo idea de cómo probar mis funciones.

Alguien tiene alguna forma de cambiar la prueba url

Tienes razón, este es de hecho un problema de jsdom. En Facebook, lo que hemos hecho para solucionar esto es usar esto:

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

esto funciona para nosotros, sin embargo, todavía estamos en jsdom 7 internamente.
Cerraré esto, ya que creo que la forma Object.defineProperty de hacer las cosas está bien. Si eso no funciona para usted en jsdom 8, me complace volver a abrirlo.

Sí, tengo algunas funciones que se ocupan de location.search y location.hash , y quiero probarlo con defineProperty como mencionaste. ¡No funcionará!

Cuando apago el modo silencioso jest , encontré esto: 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

Y ahora no tengo idea de cómo probar mis funciones.

Alguien tiene alguna forma de cambiar la prueba url

Y en mi situación, tener un campo testURL en el archivo jest.config.js puede funcionar. Pero, ¿qué pasa si quiero cambiar testURL antes de cada prueba?

Encontré esta publicación muy útil: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"En su configuración de Jest, asegúrese de configurar lo siguiente:

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

Luego, en su sección beforeEach () para su prueba, cambie la ruta según sea necesario usando
historia.pushState().

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

¡Voila! Ahora cambia su ruta para cualquier prueba, sin tener que anular ninguna configuración de jsdom como otros sugieren en el hilo mencionado anteriormente. No estoy seguro de en qué hilo encontré esta solución, ¡pero felicitaciones al desarrollador que la publicó!"


Excelente solucion!!! ¡Muchos gracias! @Mike-Tran
¡Quería una solución corta y no invasiva como esta!

Para que esto funcione a partir de junio de 2019, tuve que hacer esto:

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

Yo uso esto....

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

Probablemente parte de mi JSDOMTestWrapper pueda ayudar a alguien

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

Este enfoque funciona a partir del 27 de septiembre de 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
});

Otra solución me funciona actualmente sin escribir jsdom :

  1. Asegúrese de haber configurado testURL en jest.config.js sin importar el valor:
// jest.config.js
'testURL': 'https://someurl.com'

En su archivo de prueba:

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

Aprendido de: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking. ¡Gracias a Ryan!

no me funciona:

TypeError: la asignación a propiedades de solo lectura no está permitida en modo estricto

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

no me funciona:

TypeError: la asignación a propiedades de solo lectura no está permitida en modo estricto

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

agregue esto al archivo global.

eliminar global.ventana.ubicación;
global.ventana.ubicación = "";

Este enfoque funciona a partir del 27 de septiembre de 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
});

Estoy intentando algo similar, con location.assign, pero parece que ya no funciona.

esto funciona para mí en broma 24.9.0

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

esto funciona para mí en broma 24.9.0

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

Tuve que hacer que el código fuera asíncrono para que esto funcionara porque estaba ejecutando código dentro de una promesa.

así está funcionando ahora 😃

¿Cómo probar la ubicación de chenge en la acción 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/');

tengo error:

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

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

esto funcionó para mí

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

en broma versión 23.6.0

¿Qué es el mundial?

¿Dónde está la definición global?

Esto funcionó para mí.

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

esto crea una Ubicación con toda la funcionalidad original, pero es simulada:

beforeAll(() => {
  const location = window.location
  delete global.window.location
  global.window.location = Object.assign({}, location)
})
¿Fue útil esta página
0 / 5 - 0 calificaciones