Jest: تعذر تغيير window.location باستخدام Object.defineProperty

تم إنشاؤها على ١٩ ديسمبر ٢٠١٧  ·  78تعليقات  ·  مصدر: facebook/jest

هل ترغب في طلب _ ميزة _ أو الإبلاغ عن _ خطأ _؟ الإبلاغ عن خطأ

ما هو السلوك الحالي؟

استدعاء ما يلي من داخل مجموعة الاختبار:

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

يلقي الخطأ التالي:

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

ما هو السلوك المتوقع؟

يجب ألا يطرح الرمز استثناءً ، ويجب أن يكون تقييم window.location.hostname === "example.com" صحيحًا.

من مظهره ، يقوم الآن jsdom بتعيين window.location ليكون غير قابل للتزوير . الطريقة الوحيدة لتغيير القيم داخل window.location هي استخدام reconfigure ، لكن (لكل # 2460) Jest لا يعرض jsdom للاختبارات للتلاعب بها.

يُرجى تقديم تكوين Jest الدقيق الخاص بك وذكر Jest ، العقدة ،إصدار الغزل / npm ونظام التشغيل.

نسخة الدعابة: 22.0.1
إصدار العقدة : 8.6.0
إصدار الغزل : 1.2.0
نظام التشغيل : macOS High Sierra 10.13.2

Discussion Wontfix

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

لقد قمت بنشر حزمة جديدة على npm تسمى jest-environment-jsdom-global ، والتي قد تساعد في حل المشكلات التي يواجهها بعض الأشخاص مع Object.defineProperty .

ال 78 كومينتر

لدي مشكلة مماثلة. يمكنك إنشاء JSDOMEnvironment الخاص بك وفضح كائن jsdom إلى العالمية مثل هذا.

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

module.exports = class CustomizedJSDomEnvironment extends JSDOMEnvironment {
  constructor(config) {
    super(config);
    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;
    return super.teardown();
  }
};

وبعد ذلك يمكنك استدعاء jsdom.reconfigure في حالة الاختبار الخاصة بك كما تريد

هذا حل جيد ، شكرًا للمشاركة!

يجب أن تعيد super.teardown(); لأنه وعد ، راجع للشغل

ممتاز ، oliverzy -

هل يوجد مكان مناسب لتوثيق هذا؟ يبدو أنه سؤال يطرح بشكل معقول في كثير من الأحيان ؛ نأمل أن يتم تقليل المشكلات المستقبلية إذا تم دمج ذلك في المستندات؟

هذا الحل لم ينجح تماما .

داخل ملفات الاختبار الخاصة بنا ، يبدو أنه تم تعيين global ليكون كائن JSDom window .

بمعنى آخر ، داخل مجموعة الاختبار ، global هو نفسه window ، لكن داخل الفصل الذي يمتد JSDOMEnvironment ، يأتي global من بيئة Node.

نتيجة لذلك ، وجود هذا:

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

فشل لأن global.jsdom غير معرف.

لقد تمكنت من الالتفاف حول ذلك من خلال القيام بذلك ، لكنني لست منزعجًا جدًا من ذلك.

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

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

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

مع هذه البيئة ، يساوي global.jsdom داخل مجموعة الاختبار this.dom ، وتعمل مجموعة الاختبار أعلاه.

بالنسبة لي ، يبدو الأمر وكأن تعيين jsdom ليكون ملكية خاصة به من window لا بد أن ينهار في النهاية - هل هناك طريقة أنظف للقيام بذلك؟

تحتاج إلى كتابة jsdom بدلاً من global.jsdom في اختباراتك.

oliverzy مثل هذا؟

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

هذا يرمي jsdom is not defined ، لكن ربما أكون قد أسيء التفسير.

@ simon360 يرجى تكوين testEnvironment بالرمز من oliverzy ، راجع https://facebook.github.io/jest/docs/en/configuration.html#testenvironment -string

danielbayerlein تكوين

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

حيث @wel-ui/jest-environment-jsdom-global هو اسم الحزمة في monorepo الخاص بنا. يتم استخدام البيئة بشكل صحيح ، على الرغم من ذلك ، لأن الحل الذي يحدد jsdom على window يعمل كما هو متوقع.

راجع للشغل ، هل يعرف أي شخص لماذا لا يعمل الحل الأصلي في الإصدار الجديد؟
هذا:

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

modestfake قمنا بالترقية من JSDOM @ 9 إلى JSDOM @ 11 ، أعتقد أنهم غيروا كيفية تعريف المتغير

تضمين التغريدة تم العثور للتو على وصف لطريقة jsdom reconfigure .

تم تمييز الخاصية top في النافذة بعلامة [غير قابلة للتغيير] في المواصفات ، مما يعني أنها خاصية خاصة غير قابلة للتكوين ، وبالتالي لا يمكن تجاوزها أو تظليلها بواسطة الكود العادي الذي يعمل داخل jsdom ، حتى باستخدام Object.defineProperty.

لقد أضفت مستودعًا جديدًا لإثبات هذا السلوك. هل يستطيع أي شخص استنساخها محليًا؟

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

@ simon360 مستنسخة
image

@ simon360 لقد وجدت. لقد فاتتك الكلمة الرئيسية this عند تحديد global.jsdom :

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

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

    this.global.jsdom = this.dom;
  }

  teardown() {
    this.global.jsdom = null;

    return super.teardown();
  }
};

ماذا عن location.search ؟ لم أجد أي ذكر عنها https://github.com/tmpvar/jsdom/blob/05a6deb6b91b4e02c53ce240116146e59f7e14d7/README.md#reconfiguring -the-jsdom-with-Recfiguresettings

andrewBalekha وماذا عن هذا؟

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

شكرا modestfake - آسف للخطأ الغبي!

حسنًا ، أراه الآن - this.global على كائن بيئة Jest يتم تعيينه كـ global في ملف اختبار Jest. هذا منطقي - شكرا لمساعدتي خلال ذلك! إذا كان هناك اهتمام كافٍ ، فيمكنني حزم الإصدار الذي تم إصلاحه من هذا الريبو ووضعه على npm كـ jest-environment-jsdom-global .

ومع ذلك ، آمل أن تكون هناك طريقة أنظف للقيام بذلك في Jest في المستقبل. هذه ليست طريقة احتكاك منخفضة لتغيير window.location -

هل يمكن أن يكون هناك حظر مستندات جديد ، مثل @jest-environment ؟ فمثلا...

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

أو ربما يمكن عرض JSDom على جزء خاص من الكائن jest - شيء مثل:

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

(والتي ستكون لها فائدة إضافية تتمثل في القدرة على تغيير window.top )

لقد دمجنا # 5003 الآن. قد يكون من المنطقي أن تكون قادرًا على إضافته باعتباره docblock ، غير متأكد. cpojer؟ يمكننا أيضًا إهمال testUrl ، حيث يمكن توفيره من خلال هذا الخيار الجديد.

إذا كان هناك اهتمام كافٍ ، فيمكنني حزم الإصدار الذي تم إصلاحه من هذا الريبو ووضعه على npm كـ jest-environment-jsdom-global .

أعتقد أن هذا منطقي على أي حال ، لأنه لا يقتصر فقط على السماح لك بتعيين عنوان url - فهو يعرض JSDOM الكامل للبيئة

andrewBalekha Object.defineProperty(location, 'search', { ...options }); يطرح نفس الخطأ window.location . شكرا لهذا الاقتراح بالرغم من ذلك.

Object.defineProperty (window.location، 'href'، {
ضبط: newValue => {currentUrl = newValue ؛ } ،
}) ؛
كان لدي هذا في الإصدارات السابقة والآن يرمي خطأ.
إذا أضفت الكتابة: صحيح
يطرح استثناء آخر لا يمكنني تحديد كل من الموصل والقابل للكتابة

لقد قمت بنشر حزمة جديدة على npm تسمى jest-environment-jsdom-global ، والتي قد تساعد في حل المشكلات التي يواجهها بعض الأشخاص مع Object.defineProperty .

هل لدى أي شخص حل بديل لـ { writable: true } ؟

فمثلا:

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

...

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

...

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

danielbayerlein اقرأ هذا الموضوع. تحتاج إلى إنشاء بيئة مخصصة. الرسالة السابقة تحتوي على عنوان url مع مثال

modestfake لقد قرأت بالفعل هذا الموضوع و https://github.com/facebook/jest/issues/5124#issuecomment -352749005 يعمل بشكل جيد. لكن لدي حالة استخدام أخرى. مع Jest 21.xx لقد قمت بتعيين Object.defineProperty(window.location, 'href', { writable: true }) بدون عنوان URL - فقط { writable: true } . إذا قمت بتعيين عنوان URL ، فلن يكون الاختبار منطقيًا.

danielbayerlein ما هي حالة الاستخدام لجعلها قابلة للكتابة ولكن لا

لدي وظيفة تغير عنوان URL.

التوجيه. js

...

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

...

التوجيه. test.js

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

مع Jest 21.xx لقد قمت بتعيين Object.defineProperty(window.location, 'href', { writable: true })

أوصي بالتبديل إلى window.location.assign ، وبهذه الطريقة يمكنك السخرية من الوظيفة.

@ simon360 يعمل مثل السحر! شكرا لك. 🤝

إستعملت

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

جنبًا إلى جنب مع testURL في تكوين jest

جزء الموقع ليس هو المشكلة الوحيدة. لا يمكنني استيراد jsdom بشكل منفصل للاتصال بـ jsdom.reconfigureWindow (لأن هذه الوظيفة لم تعد موجودة في الإصدار الأخير من jsdom). كنت أفعل ذلك لاختبار الكود الذي يعمل بشكل مختلف إذا كان window !== top . لم تعد هناك طريقة لتحقيق ذلك في أحدث إصدار من jest ، والذي يستخدم إصدارًا أحدث من jsdom.

andyearnshaw طريقة jsdom.reconfigure بها عنصر windowTop في كائنها .

إذا كنت تستخدم بيئة JSDOM ( jest-environment-jsdom-global ) التي قمت بربطها أعلاه ، فيمكنك القيام بما يلي:

jsdom.reconfigure({
  windowTop: YOUR_VALUE
});

في اختباراتك لاستخلاص أفضل قيمة.

أنا أستخدم ذلك وهو يعمل بشكل رائع ، شكرًا!

في الثلاثاء ، 30 يناير 2018 ، 13:40 simon360 ، كتب [email protected] :

Almajlliss https://github.com/andyearnshaw the jsdom.reconfigure
الطريقة لها عضو windowTop في كائنها.

إذا كنت تستخدم بيئة JSDOM (jest-environment-jsdom-global)
مرتبط أعلاه ، يمكنك القيام بما يلي:

jsdom.reconfigure ({
نافذة أعلى: YOUR_VALUE
}) ؛

في اختباراتك لاستخلاص أفضل قيمة.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/facebook/jest/issues/5124#issuecomment-361595999 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/ABvdEzCLlWtzr0udscL0C6KUxpgXHZRhks5tPxvJgaJpZM4RGN7C
.

استخدام window.history.pushState و testUrl نجح معي

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

سنقوم بإغلاق هذا لأنه لا يوجد ما تفعله من جانب Jest ، والحلول موجودة (لا تتردد في مواصلة المناقشة!)

أدت الترقية من jsdom v8.5.0 إلى v11.6.2 إلى حل المشكلة بالنسبة لي. لذا فإن package.json يتضمن:

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

ينكسر إذا قمت بترقية jest و jest-cli إلى v22.2.2.

@ andr-3-w هل هناك سبب معين لوجود jsdom في package.json الخاص بك؟ تأتي مجمعة مع Jest.

SimenB ، سؤال جيد ، ليست هناك حاجة لـ jsdom في package.json . شكرا!

لذلك ، بدون استخدام jsdom مباشرة ، ها هو الحل الذي توصلت إليه لأشياءي:

يعمل لـ Jest 21.2.1 (اختبرت ذلك):

انتقل إلى إعدادات Jest (على سبيل المثال سأستخدم package.json):

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

ستتمكن الآن من تغيير كائن window.location ومن ثم يمكنك تعيين عنوان 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);
});

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

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

UserNT I لاحظت الإصدار الذي يعمل عليه هنا وأنا أستخدمه في بدلات اختبار الإنتاج على نطاق واسع. إذا لم يعمل على إصدارات أحدث ، فأنا آسف ، ابتكر الحل الخاص بك بدلاً من مجرد تقريع عشوائي.

فقط للاكتمال ، لأن الحل تقطعت به السبل في منتصف هذا الخيط ...

سيعمل حل @ petar-prog91 على Jest 21 ، لكن ليس Jest 22 مع jsdom المحدّث.

للتشغيل على أحدث Jest ، استخدم شيئًا مثل jest-environment-jsdom-global (إفصاح كامل ، هذه الحزمة الخاصة بي) لفضح الكائن jsdom واستخدم jsdom.reconfigure ، والذي سيكون به نفس التأثير (أو على الأقل تأثير مشابه).

https://github.com/facebook/jest/issues/5124#issuecomment -359411593 يعمل أيضًا على jest 22 بالنسبة لي

@ simon360 مرحبًا ، ماذا أفعل إذا احتجت إلى اختراق location.href ؟
تحتوي اختباراتي السابقة على العديد من الاختبارات مثل هذا والآن فشلت جميعها ...

const setHrefMockFn = jest.fn();
beforeAll(() => {
  Object.defineProperty(location, "href", {
    get: () => "https://xxxxx",
    set: setHrefMockFn
  });
});
it("xxx", () => {
  //...
  expect(setHrefMockFn.mock.calls[0][0]).toBe(xxx);
});

@ simon360 هل يمكن أن تعطيني مثالاً على كيفية تحديث الاختبار كما هو موضح أدناه في مكتبتك

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

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

abhijeetNmishra هل رأيت الوثائق ؟ أعتقد أن هذا سيجيب على سؤالك.

@ simon360 نعم ، بناءً على فهمي للوثائق ، يتطلب الأمر

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

الذي يتجاوز عنوان url عالميًا وليس لكل اختبار. الرجاء المساعدة!

abhijeetNmishra لست متأكدًا من أن هذه المشكلة هي أفضل مكان للمناقشة. هل تمانع في فتح مشكلة في المستودع jest-environment-jsdom-global حيث يمكننا العمل من خلالها؟ شكرا!

SimenB الحل البديل المذكور ("استخدام jest-environment-jsdom-global ") يبدو وكأنه حل دون المستوى الأمثل لما من الواضح أنه مشكلة شائعة جدًا. يحتاج أي شخص يقوم بالترقية إلى Jest 22 الآن إلى إضافة تبعية إلى حزمة الطرف الثالث هذه و (من منظور المستخدم) إعادة كتابة بعض اختباراته. هذا تراجع في سلوك جيست.

هل هناك حل لهذا يمكننا إنشاؤه في jest-environment-jsdom الافتراضي؟ يسعدني إجراء العلاقات العامة بتوجيهاتك.

من المؤسف جدًا أن يضطر شخص ما للقفز على الرغم من هذه الأطواق العديدة فقط لتغيير window.location.href. لقد بدأت للتو في استخدام Jest وأنا على وشك إعادة النظر في اختياري لإطار عمل الاختبار نظرًا لهذه المشكلة. ألا يوجد حقًا حل أفضل من الاختراقات القبيحة المقترحة أعلاه؟

ydogandjiev لا تتردد في المساهمة في المشروع لحل هذه المشكلة. تذكر أن هذا مفتوح المصدر ، لذا فإن الدخول في تعليقات مثل "غير مقبول" و "سخيف" لا يفعل شيئًا لمساعدة أي شخص.

@ msholty-fd أود المساعدة إذا استطعت. منذ أن بدأت للتو مع jest و jsdom ، لست متأكدًا من أنني أمتلك فهمًا عميقًا بما يكفي لهذه المكتبات لأعرف بالضبط أفضل طريق لتحسين هذه التجربة. هل هذا شيء أفضل معالجة في المزاح أو العدالة؟ يبدو أن إحدى هذه المكتبات أجرت تغييرًا في مرحلة ما أدى إلى كسر نهج Object.defineProperty ؛ هل كان هذا تغيير في الدعابة أو العدالة؟

إذن فهذه هي جميع الخيارات التي أعتبرها مفضلة بناءً على عدد الأسطر المطلوبة لتغيير window.location ؛ لا يعمل أي من هؤلاء حاليًا:

  1. لا يعمل إعداد href مباشرةً لأن jsdom يطرح استثناءً للرسالة "خطأ: لم يتم التنفيذ: التنقل (باستثناء تغييرات التجزئة)":
    window.location.href = "https://www.example.com";
  1. لا يعمل استخدام Object.defineProperty لأن JSDOM جعل خاصية موقع النافذة [غير قابلة للتزوير] :

    Object.defineProperty(window.location, "href", {
      value: "https://www.example.com",
      configurable: true
    });
    
  2. لا يعمل إنشاء مثيل jsdom وتكوينه لأن Jest يبدو أنه يستخدم مثيله الخاص الذي لم يتم الكشف عنه لسهولة الوصول إليه:

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

يتطلب عمل الخيار 1 أو 2 عمل jsdom للتراجع عن أهدافهم الحالية المتمثلة في التصرف كمتصفح حقيقي. لذلك ، يبدو أن الخيار الوحيد المتاح لدينا هو تسهيل إعادة تكوين مثيل jsdom الذي يستخدمه jest. هل من المنطقي فضح هذا المثال مباشرة على كائن الدعابة العالمية ؛ أي السماح بشيء مثل هذا:

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

ما زلت أعتقد أن عمل location.assign('some-url') أفضل من location.href = 'some-url' . أجد استدعاء دالة أكثر وضوحًا من التخصيص ، ويمكنك السخرية من الوظيفة

SimenB في الحالات التي يحاول فيها الرمز _set_ location.href ، نعم ، location.assign() أفضل. ولكن إذا كنت تختبر السلوك الذي _reads_ location.href ، location.assign() لا يحل المشكلة ، خاصة وأن location.assign() في JSDOM لا يفعل شيئًا في الواقع.

الفكرة من استخدام reconfigure هي تنشيط مسار رمز يتم تمكينه فقط عند تشكيل location.href بطريقة معينة. في حالتنا ، كان لدينا بعض الأكواد التي تغيرت اعتمادًا على المجال الحالي - الشفرة كريهة الرائحة ، نعم ، لكنها ضرورية أيضًا ، وأفضل طريقة لتخفيف الشفرة ذات الرائحة الكريهة هي أن يكون لدينا تركيبات اختبار تلتقط السلوك وتضمن بقائه في المكان.

هل هناك طريقة لاستخدام هذا مع الإنزيم والمكون المركب لاختبار عمليات إعادة التوجيه؟

اجتاز الاختبار أدناه قبل ترقية Jest:

""
it ('التوجيهات إلى المسار الصحيح'، () => {

Object.defineProperty(window.location, 'href', {
  writable: true,
  value: 'https://mysuperawesomesite.com/',
});

const component = mount(
  <App {...props} />
);

const link = component.find('.class');

link.simulate('click');

expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');

}) ؛

After upgrading Jest and implementing [jest-environment-jsdom-global](https://www.npmjs.com/package/jest-environment-jsdom-global), I tried the following to no avail:

  ```
it('routes to correct route', () => {

    jsdom.reconfigure({
      url: 'https://mysuperawesomesite.com/',
    });

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.href).toEqual('https://mysuperawesomesite.com/new');
  });

(لا يزال window.location.href يساوي " https://mysuperawesomesite.com/ " ، ولم يتم تغييره إلى ("https://mysuperawesomesite.com/new").

لا يتم إعادة توجيه حدث النقر على العنصر عند استخدام هذه الطريقة ، وتحدث إعادة التوجيه من خلال ضبط window.location.href.

من غير الواضح كيفية اختبار هذا بشكل صحيح أو ما إذا كانت الاختبارات التي استخدمت Object.defineProperty من قبل سيئة الإنشاء من البداية. شكرا مقدما لأي مساعدة.

تحرير: تم حلها

كان قادرًا على حل هذه المشكلة باستخدام window.location.assign (url) بدلاً من window.location.href = href. سمح لي هذا بإخراج طريقة التعيين واختبار ما إذا كان يتم استدعاؤها بشكل صحيح. انظر أدناه:

it('routes to correct route', () => {
    window.location.assign = jest.fn();

    const component = mount(
      <App {...props} />
    );

    const link = component.find('.class');

    link.simulate('click');

    expect(window.location.assign).toBeCalledWith('https://mysuperawesomesite.com/new');
    window.location.assign.mockRestore();
  });

SimenB هناك فرق كبير بين .assign و .href يمكنك القراءة على MDN. الأول لديه قيود رئيسية عبر المجال. في الكود الخاص بي ، أريد إعادة توجيه الصفحة الرئيسية من iframe حيث يتم تشغيل الكود الخاص بي. هذه مجالات متقاطعة. والطريقة الوحيدة للقيام بذلك هي تغيير href .
أرغب في إعادة فتح هذه المشكلة ، لأنني لا أرى حلًا بديلًا حاليًا وسأضطر فقط إلى عدم إجراء اختبار لهذه الوظيفة. الذي تمتص بوضوح.

أنا في نفس المركب مثلsoswow. سيكون من الرائع أن تتمكن من توفير آلية لتجاوز عنوان url ، حيث أقوم أيضًا بإزالة العديد من اختبارات الوحدة حتى يتم استعادة هذه الوظيفة.

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

لا يوجد شيء يمكننا فعله من ناحية الدعابة. أنا متأكد من أن jsdom سيحب العلاقات العامة التي تدعمه. https://github.com/jsdom/jsdom/issues/2112

إليك حل بسيط يعمل.

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

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

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

vastus كما أشرت صراحة - في المجالات المتقاطعة . لن تسمح واجهة برمجة التطبيقات history API بالتبديل إلى مجال مختلف بقدر ما أتذكر.

نظرًا لأنه لا يمكن تجاوز location مباشرة على كائن jsdom window ، فإن إحدى الطرق الممكنة هي تجاوزه على كائن مشتق:

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

أستخدم هذا لحل المشكلة:

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

مستوحى من RubenVerborgh & annemarie35

إضافة مثال لاختبار location.search بناءً على حل vastus :

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

RubenVerborgh يعمل مثل السحر.

يحاول:

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

حل مشابه لـ sahalsaad :
`` جافا سكريبت
const oldWindow = window.location ؛
حذف window.location ؛
window.location = {
... النوافذ القديمة ،
// تضمين أي عمليات الكتابة فوق مخصصة مثل كعب sinon التالي
استبدل: sinon.stub () ،
} ؛

// افعل سحرك

window.location = oldWindow ؛
""

sahalsaad شكرا! لقد استخدمت شكلًا مختلفًا من الحل الخاص بك لمحاكاة window.location.search:

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

ربما يكون حلاً أفضل:

import { URL } from 'whatwg-url';

const location = new URL(window.location.href);
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()

delete window.location
window.location = location

قمت بحل مشكلتي باستخدام الحل المقدم من أسخر من window.location.search . لذلك اعتدت

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

نظرًا لأنه لا يمكن تجاوز location مباشرة على كائن jsdom window ، فإن إحدى الطرق الممكنة هي تجاوزه على كائن مشتق:

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

إجابتك هي الوحيدة التي تعمل لوضعي ، شكرا!

نظرًا لأنه لا يمكن تجاوز location مباشرة على كائن jsdom window ، فإن إحدى الطرق الممكنة هي تجاوزه على كائن مشتق:

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

إجابتك هي الوحيدة التي تعمل لوضعي ، شكرا!

هذا لم يعد يعمل 😢

نظرًا لأنه لا يمكن تجاوز location مباشرة على كائن jsdom window ، فإن إحدى الطرق الممكنة هي تجاوزه على كائن مشتق:

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

إجابتك هي الوحيدة التي تعمل لوضعي ، شكرا!

هذا لم يعد يعمل 😢

لم يعمل معي ايضا

لقد قمت بحل هذا من خلال القيام بما يلي:

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

أنا أستخدم النموذج التالي كأداة مساعدة للعمل مع Location

export class MockLocation extends URL implements Location {
  ancestorOrigins: any = []
  toString = jest.fn().mockImplementation(() => this.toString())
  assign = jest.fn(href => this.href = href)
  replace = jest.fn(href => this.href = href)
  reload = jest.fn()

  constructor(
    url: string = 'http://mock.localhost',
  ) {
    super(url)
  }

  onWindow(window: Window) {
    Object.defineProperty(window, 'location', { 
      writable: true,
      value: this
    });
    return this
  }
}

ثم في اختباراتي

let location: MockLocation

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

وجدت نفسي أقوم بضرب أشياء خادعة مثل هذه طوال الوقت ، وأنشأت وظيفة مساعد مرنة:

export const safelyStubAndThenCleanup = (target, method, value) => {
  const original = target[method]
  beforeEach(() => {
    Object.defineProperty(target, method, { configurable: true, value })
  })
  afterEach(() => {
    Object.defineProperty(target, method, { configurable: true, value: original })
  })
}

ثم الاستخدام:

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

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

ويمكنك إيقاف كل ما تريد: pathname ، href ، إلخ ... يمنحك هذا فائدة مجانية إضافية للتنظيف.

المفتاح هو أنه لا يمكنك العبث بـ location نفسه ، لذلك فقط استبدل location بمزيف ، ثم أعده مرة أخرى عند الانتهاء من الاختبار.

كما أرى في جلسة التصحيح الخاصة بي ، يتم تنفيذ global.location عبر getter ولكن ليس من خلال خاصية بسيطة. ألن يكون من الآمن إعادة تعريفه على هذا النحو؟

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

على الرغم من صعوبة تخيل سبب رغبتي في استخدام global.location الأصلي ، إلا أنه يبدو أكثر صحة قليلاً.
وهذا الرمز يعمل بشكل جيد بالنسبة لي ، بالطبع. لقد قمت للتو بالوصول إلى location.pathname ، ولكن هذا الكائن يمكن أن يمتد بسهولة ببعض jest.fn() إذا لزم الأمر.

لقد كان هذا يعمل بالنسبة لي ، باستخدام jest 26.5

function stubLocation(location) {
  beforeEach(() => {
    jest.spyOn(window, "location", "get").mockReturnValue({
      ...window.location,
      ...location,
    });
  });
}

stubLocation({ pathname: "/facebook/jest/issues/5124" });

test("mocks location prop", () => {
  expect(window.location.pathname).toEqual("/facebook/jest/issues/5124");
});

إضافة مثال لاختبار location.search بناءً على حل vastus :

  test('gets passed query param and returns it as a string if it exists', () => {
    history.replaceState({}, 'Test', '/test?customer=123');
    const customerId = getQueryParam('customer');
    expect(customerId).toBe('123');
  });

لقد نجح هذا تمامًا مع المشكلة التي كنت أواجهها

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات