Jest: window.location.href لا يمكن تغييره في الاختبارات.

تم إنشاؤها على ١٣ أبريل ٢٠١٦  ·  70تعليقات  ·  مصدر: facebook/jest

مرحبًا cpojer ،

هذا في الواقع أكثر من مشكلة jsdom@8 ... راجع tmpvar / jsdom # 1388 ، لكنني أريد التثبيت هنا أيضًا حتى يلتقط Jest أي حل يأتي به jsdom.

في السابق باستخدام [email protected]/[email protected] ، كان بإمكانك كتابة اختبار مثل هذا:

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

وسيجتاز هذا الاختبار. منذ jsdom@8 لم يعد هذا ممكنًا وتفشل هذه الاختبارات.

يبدو أن jsdom يبحث في نوع من الإمكانيات ، أردت فقط التأكد من أن Jest ستلتقط هذه الإمكانية عندما تكون متاحة.

التعليق الأكثر فائدة

أنت محق ، هذه بالفعل مشكلة jsdom. في Facebook ، ما فعلناه للتغلب على هذا هو استخدام هذا:

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

هذا يعمل بالنسبة لنا ، لكننا ما زلنا في jsdom 7 داخليًا.

سأغلق هذا ، لأنني أعتقد أن طريقة Object.defineProperty لعمل الأشياء جيدة. إذا لم يفلح ذلك في jsdom 8 ، فأنا سعيد بإعادة فتحه.

ال 70 كومينتر

أنت محق ، هذه بالفعل مشكلة jsdom. في Facebook ، ما فعلناه للتغلب على هذا هو استخدام هذا:

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

هذا يعمل بالنسبة لنا ، لكننا ما زلنا في jsdom 7 داخليًا.

سأغلق هذا ، لأنني أعتقد أن طريقة Object.defineProperty لعمل الأشياء جيدة. إذا لم يفلح ذلك في jsdom 8 ، فأنا سعيد بإعادة فتحه.

رائع ، شكرًا سأجرب هذا.

cpojer ، لا يمكنني معرفة ما أحتاجه للنقر عليه لإعادة فتح هذه المشكلة ...

هل هناك على أي حال في بيئة jest لشخص ما للاتصال بـ jsdom.changeUrl(window, url) كما هو موضح هنا https://github.com/tmpvar/jsdom#changing -the-url-of-an-existing-jsdom- مثيل النافذة في [email protected] ؟

تذكرة قديمة ولكن بالنسبة لأولئك الذين ما زالوا يواجهون هذه المشكلة ، فقد بدأنا في استخدام window.location.assign() بدلاً من ذلك ، لذا في اختباراتنا يمكننا السخرية من وظيفة assign مثل ذلك ..

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

شكرا @ th3fallen . هذا بارد!

راجع للشغل cpojer أبدأ في FB في 1 مايو .... ؛ ص

لطيف!

أحاول ترحيل اختباراتنا من Mocha + Chai + Sinon.js إلى Jest ولا يمكنني معرفة كيفية تغيير الموقع لاختبار معين.
يستخدم Jest 19.x JSDom 9.12 الذي لا يسمح بتغيير الموقع باستخدام خدعة Object.defineProperty . أيضًا ، لا يمكنني استخدام jsdom.changeURL() للأسباب الموضحة في tmpvar / jsdom # 1700.
cpojer ماذا عن تنفيذ طريقة الوكيل لـ jsdom.changeURL() في Jest؟

okovpashko نخطط لفضح jsdom للبيئة: https://github.com/facebook/jest/issues/2460

Object.defineProperty يعمل معنا في FB.

thymikee لقد رأيت هذه المشكلة لكنني اعتقدت أن الاقتراح قد تم رفضه.
cpojer لقد أخطأت في قراءة مثالك وخلطته مع أمثلة أخرى متعلقة بهذه المشكلة ، حيث اقترح الناس استخدام Object.defineProperty(window, 'location', {value: 'url'}); . شكرا لك!

لا أحتاج إلى تغيير href فقط ، لذلك كتبت طريقة بسيطة ، قد تكون مفيدة لمن سيقرأ هذا الموضوع:

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

أعتذر عن سحب هذا الموضوع أكثر ، لكنني حاولت الاستهزاء بوظيفة الدفع كما هو مقترح ...

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

لكنني ما زلت أتلقى خطأ jsdom لا يبدو أنه يمكنني الالتفاف حوله:

       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

أدرك أن هذا خطأ jsdom ، ولكن بالنسبة لأولئك الذين قاموا بحل هذا ، هل هناك أي سياق إعداد آخر يمكنك مشاركته والذي قد يتيح لي الالتفاف حول هذا؟

شكرا

@ مات دالتون جرب اقتراحي في https://github.com/facebook/jest/issues/890#issuecomment -295939071 يعمل جيدًا بالنسبة لي

@ مات دالتون ما هو عنوان URL الخاص بك؟ هل تم تعيين عنوان URL الخاص بك في jest-config.json أم يتم تهيئته كـ about:blank ؟

ianlyons نعم لقد قمت بتعيين قيمة "https://test.com/" لهذا في package.json ، ولا تظهر أي من المسارات كـ blank .

@ th3fallen إذا كنت أفهمك بشكل صحيح ، لا أعتقد أن هذا يعمل مع حالة الاستخدام الخاصة بي. هل تقوم بتمرير عنوان url كقيمة سياق تؤدي إلى تشغيل التعيين؟ أحاول وضع اختبار تكامل أولي معًا ، لذلك أريد التحقق من كيفية استجابة جهاز التوجيه لتحميل البيانات الأولية. لقد سخرت من استجابة واجهة برمجة التطبيقات ، ثم أحتاج إلى إجراء تغيير عنوان URL باستخدام منطق التطبيق (أي لا أريد تشغيله خارجيًا بنفسي).

يبدو أن Object.defineProperty يقوم بالحيلة لاختبار الوظيفة التي تعتمد على window.location.search ، على سبيل المثال. ومع ذلك ، فإنه يغير window.location.search لذلك قد تتأثر الاختبارات الأخرى. هل هناك طريقة "للتراجع" عن التغييرات التي أجريتها على window.location.search عبر Object.defineProperty ، نوعًا ما ، وظائف وهمية مثل jest لها وظيفة mockReset ؟

@ msholty-fd يمكنك تجربة هذا النهج:

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

توقف عن العمل في Jest 22.0.1

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

رسالة خطأ:

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

حسنًا ، قد نحتاج إلى السماح للأشخاص بطريقة ما بالاتصال بـ reconfigure . https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring-the-jsdom-with-Recfiguresettings

تم فتح مشكلة جديدة متعلقة بهذا ، حيث تم إغلاق هذه المشكلة: # 5124

SimenB لست مقتنعًا بأن Jest يجب أن يصلح هذا. يجب أن يسمح JSDOM window.location.assign() بالعمل على النحو المنشود وإعادة تكوين ناتج window.location.href وما إلى ذلك.

حصلت على TypeError: Could not parse "/upgrades/userlogin?hardwareSku=sku1351000490stgvha" as a URL لأن jsdom لديه عنوان URL أساسي افتراضي هو about:blank

حاولت تعيين عنوان url أساسي لـ jsdom ، وقضيت 4 ساعات عليه دون نجاح (أعرف كيفية القيام بذلك ، فقط أدخل <base href='your_base_url' /> إلى dom ؛ ولكن ، تم إنشاء dom بواسطة jest ، ليس من قبلي ، لذا استسلمت.

الحل Object.defineProperty يعمل فقط مع الإصدار القديم jsdom (تحصل على "لا يمكن إعادة تعريف خطأ الخاصية مع الإصدار الأحدث من jsdom ) ؛
إذا كنت تستخدم jsdom ver> 10 ، لأن @ th3fallen المذكور هو الحل الصحيح.
استخدام window.location.assign هو الطريق الصحيح للذهاب

إذا كنت تريد عنوان url آخر غير about:blank ، فيمكنك استخدام testURL config.

شكرا SimenB على ردكم.

لا ، كنت أتحدث عن base url وليس url . لديّ رمز يعمل على تنفيذ window.location.href="/login" وعند تشغيل jest ، jsdom استثناءً يشكو /login ليس عنوان url صالحًا

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

لقد تحققت من الكود المصدري لـ jsdom وأدركت أن هذا بسبب عدم وجود إعداد عنوان url أساسي (هذا يعادل كتابة "/ login" في شريط عنوان URL للمتصفح بدون عنوان أساسي).

باستخدام jsdom ، يمكننا عادةً إعداد عنوان url أساسي عبر

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

ولكن نظرًا لأن jest أنشأ jsdom ، فهذا خارج عن إرادتنا.
--- تحديث: أفترض أنه يمكنني صراحة إضافة jsdom كتبعية وتكوين jsdom يدويًا.

ثم وجدت حلاً يتمثل في استبدال window.location.href= بـ window.location.assign و محاكاة assign وظيفة وقد نجحت معي

@ bochen2014 يحتوي هذا الإصدار على مزيد من المعلومات حول كيفية استخدام الإصدار الأحدث من jsdom: # 5124

tl ؛ dr: يمكنك محاكاة window.location.assign() ، أو يمكنك استخدام jest-environment-jsdom-global ، والذي سيسمح لك بإعادة تكوين jsdom في الرحلة.

شكرا @ simon360

هذا ما فعلته ؛-)
لقد استخدمت jsdom.reconfigure لإعداد مبدئي مختلف لـ urls في اختباراتي ، وكلما احتجت إلى تغيير عنوان url في الكود (وليس الاختبار) ، أستخدم window.location.assign وسخرت منه. الذي عمل لي.

فقط للأشخاص الذين قد يواجهون نفس المشكلة ، لتعيين عنوان url الخاص بـ jsdom

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

لاحظ أن هذا سيحدد عنوان url لجميع اختباراتك ؛
إذا كنت تريد عنوان url مختلفًا في بعض الاختبارات المحددة ، فاستخدم jsdom.reconfigure api؛
إذا كنت بحاجة إلى تغيير عنوان url سريعًا خارج كود اختبار الوحدة (أي رمز الإنتاج) ، فأنت بحاجة إلى استخدام window.location.assign والسخرية منه.

نشرها على تذكرة أخرى ، لكنني سأقوم بنشرها هنا:

وجدت حلًا جيدًا لـ Jest 21.2.1

حسنًا ، أسهل حل لهذا حتى الآن هو:
انتقل إلى إعدادات Jest (على سبيل المثال سأستخدم package.json):

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

الآن سيكون لديك حق الوصول إلى كائن النافذة وبعد ذلك يمكنك تعيين عنوان URL على ما تريد أثناء الاختبارات.

it('Should set href url to testURL', () => {
    // Here I set href to my needs, opinionated stuff bellow
    const newUrl = 'http://localhost/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
    Object.defineProperty(window.location, 'href', {
        writable: true,
        value: newUrl
    });

    console.log(window.location.href);
});

it('Should set pathname url to testURL', () => {
    // Here I set href to my needs, opinionated stuff bellow
    const newUrl = '/editor.html/content/raiweb/it/news/2018/02/altered-carbon-best-cyberpunk-tv-series-ever.html';
    Object.defineProperty(window.location, 'pathname', {
        writable: true,
        value: newUrl
    });

    console.log(window.location.pathname);
});

نأمل أن يساعد هذا شخص ما.

@ petar-prog91 كان ذلك مفيدًا. على الرغم من وجود خطأ إملائي - يجب أن يكون testURL وليس TestURL

BarthesSimpson شكرًا على الإشعار ، التعليق المحدث.

توقف عن نشر هذا ، فهو لا يعمل على الدعابة ":" ^ 22.4.2 "

مرحبا،
لقد استخدمت هذا في الاختبار ، وقمت بحذف الحالة العامة وإنشاء حالة جديدة باستخدام 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 أكد - أنه يعطي TypeError: Cannot redefine property: href

@ annemarie35 لا يعمل - ReferenceError: JSDOM is not defined

لا أعرف ما إذا كان هذا سيساعد شخصًا ما ، ولكن هذا ما أفعله حاليًا.

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

اكتب وظيفة إعادة التوجيه واستخدمها بدلاً من ذلك. لذلك في اختبار env ، سوف يعتمد على jsdom.reconfigure url لتغيير جزء عنوان url.

أنا أستخدمه مثل هذا

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

بعد ذلك ، في اختبارك ، يمكن أن يكون شيء من هذا القبيل

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

انتهى بي الأمر بفعل هذا الأمر الذي نجح:

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

لدي هذا أعلى ملف إعداد 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;

تم إصلاحه عن طريق تعيين "testURL": " http: // localhost / " في تكوين Jest (أنا أستخدم أحدث إصدار). بشكل افتراضي ، يكون " about: blank " وكان يتسبب في حدوث خطأ JSDOM (لا يمكنك تغيير عنوان url "about: blank" إلى شيء آخر).

موارد:
http://jestjs.io/docs/en/configuration#testurl -string
https://github.com/jsdom/jsdom/issues/1372

لقد وجدت أن هذا المنشور مفيد للغاية: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"في تهيئة Jest لديك ، تأكد من تعيين ما يلي:

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

ثم في قسم beforeEach () للاختبار الخاص بك ، قم بتغيير المسار حسب الحاجة باستخدام
history.pushState ().

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

هاهو! يمكنك الآن تغيير المسار الخاص بك لأي اختبار ، دون الحاجة إلى تجاوز أي تكوينات jsdom كما يقترح الآخرون في الموضوع المذكور أعلاه. لست متأكدًا من أي سلسلة رسائل وجدت هذا الحل عليها ، ولكن مجدًا للمطور الذي نشره! "

@ مايك تران أنت صخرة! لقد نجح ذلك تمامًا ، وبسيط جدًا. لم أضطر حتى إلى استخدام إعداد testURL.

@ مايك تران هذا يعمل! شكرا لك! ومع ذلك ، لم أكن بحاجة إلى testURL أو beforeEach . فعلتها للتو:

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

والآن لا يتعين علي استخدام Object.defineProperty بعد الآن 😅

jcmcneal شكرا الذي فعل ذلك من أجلي! (jest 23.0.0)

إذا كان هدفك هو السخرية من الكائن window ، فإليك الحل (ليس أنيقًا جدًا ، ولكنه يعمل):

قم بإنشاء واجهة (لست متأكدًا مما إذا كانت الواجهة هي الكلمة الصحيحة ، ولكن أتمنى أن تحصل على النقطة) فئة:

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

في الكود الفعلي ، استبدل window بطريقة الواجهة ، على سبيل المثال win

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

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

ثم ، في اختبارات الدعابة ، أنت تسخر منهم فقط حتى لا يشتكي jsdom. يمكنك حتى تأكيدها:

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

@ مايك تران ، jcmcneal أشكر! جميع الأعمال على النحو المطلوب!

تقوم الفئة SSOtestComponent بتوسيع 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 ؛

أنا أكتب حالة اختبار الوحدة باستخدام إنزيم ومازح كيف ستكتب نافذة الشرط.الموقع ... الرجاء إعطاء الإجابة

هذا عمل لي

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

على الإصدار jest 23.6.0

هذا عمل لي.

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

تضمين التغريدة

شكراFelipeBohnertPaetzold. كنت أستخدم location.host في الكود الخاص بي ، لذلك وجدت أنني بحاجة إلى كائن موقع كامل ، لذلك كان ما يلي يعمل بشكل أفضل بالنسبة لي ، بدلاً من الاضطرار إلى تمرير كل خاصية موقع يدويًا:

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

لاحظ أن هذا يعمل في Node 6.13+ (راجع مستندات فئة URL ) وكنت أستخدم Jest 24.

لاحظ أيضًا أن هذا لا يعمل مع عناوين URL النسبية ، راجع https://url.spec.whatwg.org/#example -url-parsing.

يعمل TypeScript هذا بالنسبة لي على Jest 24.0.0 و 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();
        });
    });
});

تضمين التغريدة

مرحبًا يا رجل) استخدام رائع!

بالنسبة لي ، كانت الاختبارات في src/setupTests.test.ts زائدة عن الحاجة قليلاً ، لأنك بالفعل اختبرت mockWindow بالكامل في src / testUtils.test.ts. لذلك ، في اختبارات src/setupTests.ts يكفي أن تقوم باستدعاء mockWindow باستخدام الوسيطات الصحيحة.

شكرا لك)

tzvipm @ jup-iter شكرا على 👍. لقد أصدرت للتو @jedmao/storage و @jedmao/location ، وكلاهما محايد تمامًا لمفهوم الدعابة. يجب أن تكون قادرًا على spyOn الطرق المناسبة دون كتابة أي اختبارات إضافية ، حيث يتم اختبار حزم npm بالكامل.

في حالة ظهور هذا الخطأ عند استخدام Vue ، ما عليك سوى استخدام this.$router.push({...}) بدلاً من this.$router.go({...})

image

ضع الكود أدناه في السطر 1:
delete global.window.location;
global.window.location = "";

يمكن الآن التقاط حدث نقرة يغير موقع window.location.

"مضحك": "^ 23.6.0"
الإصدار 10.15.0
6.5.0

هذا يعمل:

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

أو الأفضل من ذلك...

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

أنت محق ، هذه بالفعل مشكلة jsdom. في Facebook ، ما فعلناه للتغلب على هذا هو استخدام هذا:

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

هذا يعمل بالنسبة لنا ، لكننا ما زلنا في jsdom 7 داخليًا.

سأغلق هذا ، لأنني أعتقد أن طريقة Object.defineProperty لعمل الأشياء جيدة. إذا لم يفلح ذلك في jsdom 8 ، فأنا سعيد بإعادة فتحه.

نعم ، لدي بعض الوظائف التي تتعامل مع location.search و location.hash ، وأريد اختبارها بـ defineProperty كما ذكرت. لن تنجح!

عندما أوقف تشغيل الوضع الصامت jest ، وجدت هذا: 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

والآن ليس لدي أي فكرة عن كيفية اختبار funtions الخاصة بي.

أي شخص لديه أي طريقة لتغيير الاختبار url

أنت محق ، هذه بالفعل مشكلة jsdom. في Facebook ، ما فعلناه للتغلب على هذا هو استخدام هذا:

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

هذا يعمل بالنسبة لنا ، لكننا ما زلنا في jsdom 7 داخليًا.
سأغلق هذا ، لأنني أعتقد أن طريقة Object.defineProperty لعمل الأشياء جيدة. إذا لم يفلح ذلك في jsdom 8 ، فأنا سعيد بإعادة فتحه.

نعم ، لدي بعض الوظائف التي تتعامل مع location.search و location.hash ، وأريد اختبارها بـ defineProperty كما ذكرت. لن تنجح!

عندما أوقف تشغيل الوضع الصامت jest ، وجدت هذا: 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

والآن ليس لدي أي فكرة عن كيفية اختبار funtions الخاصة بي.

أي شخص لديه أي طريقة لتغيير الاختبار url

وفي وضعي ، قد يعمل حقل testURL في ملف jest.config.js . ولكن ماذا لو أردت تغيير testURL قبل كل اختبار.

لقد وجدت أن هذا المنشور مفيد للغاية: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking

"في تهيئة Jest لديك ، تأكد من تعيين ما يلي:

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

ثم في قسم beforeEach () للاختبار الخاص بك ، قم بتغيير المسار حسب الحاجة باستخدام
history.pushState ().

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

هاهو! يمكنك الآن تغيير المسار الخاص بك لأي اختبار ، دون الحاجة إلى تجاوز أي تكوينات jsdom كما يقترح الآخرون في الموضوع المذكور أعلاه. لست متأكدًا من أي سلسلة رسائل وجدت هذا الحل عليها ، ولكن مجدًا للمطور الذي نشره! "


حل ممتاز !!! شكرا جزيلا لك! @ مايك تران
كنت أرغب في حل قصير وغير جائر مثل هذا!

لكي يعمل هذا اعتبارًا من يونيو 2019 ، كان علي القيام بذلك:

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

انا استعمل هذا....

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

ربما يمكن أن يساعد جزء من JSDOMTestWrapper شخصًا ما

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

يعمل هذا النهج اعتبارًا من 27 سبتمبر 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
});

حل آخر يعمل معي حاليًا بدون كتابة jsdom :

  1. تأكد من تعيين testURL في jest.config.js بغض النظر عن القيمة:
// jest.config.js
'testURL': 'https://someurl.com'

في ملف الاختبار الخاص بك:

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

المستفادة من: https://www.ryandoll.com/post/2018/3/29/jest-and-url-mocking. من ثانسك إلى ريان!

لا تعمل لدي:

TypeError: التعيين لخصائص للقراءة فقط غير مسموح به في الوضع المتشدد

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

لا تعمل لدي:

TypeError: التعيين لخصائص للقراءة فقط غير مسموح به في الوضع المتشدد

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

أضف هذا إلى الملف العمومي.

حذف global.window.location ؛
global.window.location = "" ؛

يعمل هذا النهج اعتبارًا من 27 سبتمبر 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
});

أحاول شيئًا مشابهًا ، مع تعيين الموقع ، ولكن يبدو أن هذا لم يعد يعمل.

هذا يعمل بالنسبة لي على 24.9.0 مازحة

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

هذا يعمل بالنسبة لي على 24.9.0 مازحة

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

اضطررت إلى جعل الكود غير متزامن من أجل تشغيل هذا لأنني كنت أقوم بتشغيل الكود ضمن وعد.

هكذا تعمل الآن 😃

كيفية اختبار موقع chenge في عمل 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/');

لدي خطأ:

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

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

هذا عمل لي

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

على الإصدار jest 23.6.0

ما هو العالمية؟

اين التعريف العالمي؟

هذا عمل لي.

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

يؤدي هذا إلى إنشاء موقع به جميع الوظائف الأصلية ، ولكن يمكن السخرية منه:

beforeAll(() => {
  const location = window.location
  delete global.window.location
  global.window.location = Object.assign({}, location)
})
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات