Protractor: عمل روتيني (أساسي): إضافة رسالة خطأ أفضل لعمليات إعادة التوجيه من جانب العميل

تم إنشاؤها على ٢٨ أكتوبر ٢٠١٥  ·  49تعليقات  ·  مصدر: angular/protractor

إذا نقر الاختبار على ارتباط أو شيء يتسبب في إعادة توجيه خارج browser.get أو browser.refresh ، فحينئذٍ في المرة التالية التي يُطلق فيها اسم waitForAngular على المستخدم ، من المحتمل أن يواجه رسالة الخطأ هذه ( نظرًا لأننا لم ننتظر تحميل Angular) ، فهذا ليس مفيدًا جدًا.

يجب إخبار المستخدمين بما يحدث من خطأ وحول الحلول المحتملة (على سبيل المثال ، استخدام browser.get أو browser.refresh لإصلاح التمهيد).

debuggability

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

sjelin ، هل

حالة الاستخدام الخاصة بي: لدي اختبار يُدخل اسم المستخدم / كلمة المرور ويضغط على زر تسجيل الدخول ، والذي يعيد التوجيه إلى صفحة أخرى عند تسجيل الدخول بنجاح.

ال 49 كومينتر

sjelin ، هل

حالة الاستخدام الخاصة بي: لدي اختبار يُدخل اسم المستخدم / كلمة المرور ويضغط على زر تسجيل الدخول ، والذي يعيد التوجيه إلى صفحة أخرى عند تسجيل الدخول بنجاح.

إذا كانت كلتا الصفحتين ذات زوايا ، فيجب عليك فقط إضافة browser.refresh بعد تحميل الصفحة الجديدة لإصلاح المشكلة

نحن نعمل على ميزة لتحسين هذا في المستقبل

لقد كنت أعاني من هذه المشكلة منذ أسبوع الآن. browser.refresh() لا يعمل إذا كنت أفعل POST.

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

في الواقع ، أنا أستخدم شيئًا كهذا كحل بديل أعلم أنه سيكون هناك إعادة توجيه:

(function (expectedUrl, timeout) {
    var loaded = false;

    browser.wait(function () {
        browser.executeScript(function () {
            return {
                url: window.location.href,
                haveAngular: !!window.angular
            };
        }).then(function (obj) {
            loaded = (obj.url == expectedUrl && obj.haveAngular);
        });

        return loaded;
    }, timeout);
})(expectedUrl, timeout);

tassoevan ، هذا مثالي! هذا ما كنت أتوقعه في انتظار أن يقوم به Angular - انتظر حتى تكون الزاوية متاحة.

عندما تبدأ المنقلة ، فإنها تنتقل دائمًا إلى baseUrl ثم إلى صفحة الاختبار الخاصة بي. هل يعتبر ذلك إعادة التوجيه التي تتحدث عنها؟

ليس تمامًا patrickliechty ، أعني عندما تنقر على رابط (مثل <a href='google.com'> ) ويوجه المتصفح إلى google.cm

juliemr مع hankduan ذهب ، هل هناك أي شخص يعمل على الإصلاح على المدى الطويل لهذا بعد الآن؟

sjelin ، هل هذه المشكلة لا تزال موجودة في المنقلة 3.1.1 يمكنني رؤية ذلك

+1 لإصلاح أفضل. ركض في هذا اليوم دون أي فكرة عن كيفية حلها بشكل موثوق. شكر!

نفس المشكلة !!! شكرا tassoevan لإصلاح موثوق

+1 للإصلاح.

لقد حصلت على هذا الخطأ عن طريق عمل العنصر (). getText () خارج المواصفات (بدون حدوث browser.get ()) - كان يحاول إنشاء اختصار لإخراج div معين خارج المواصفات. حولته إلى وظيفة وكان كل شيء على ما يرام.

أحصل على هذا الخطأ عند التشغيل ضد \ node_modules \ منقلة \ example \ conf.js ، بالتأكيد يجب أن يعمل مثال conf.js دائمًا بغض النظر عن ؟؟

بالنسبة للأشخاص الذين يستخدمون المنقلة بدون Angular ، هل هناك حل بديل؟

Overdrivr يمكنك ضبط browser.ignoreSynchronization = true ؛ لإخبار المنقلة بعدم انتظار Angular.

browser.ignoreSynchronization = صحيح بالنسبة لي عند التبديل بين النوافذ.

حسنًا ولكن بعد ذلك تحتاج إلى ضبط browser.ignoreSynchronization = false ؛

أتلقى هذا الخطأ ويحتوي على رابط لمشكلة جيثب هذه.

هذا ما لدي:

/**
 * Waits for MR job to complete.
 * <strong i="7">@param</strong> {string} url The GET URL to kick off the map reduce job.
 * <strong i="8">@return</strong> {!webdriver.promise.Promise}
 */
function waitForMapReduceJobToComplete(url) {
  browser.ignoreSynchronization = true;
  browser.get(url);
  $('a[href*="/mapreduce"]').click();
  browser.wait(()=> {
    console.log('wait');
    return $('#auto-refresh').isPresent();
  });
  browser.controlFlow().execute(function() {
    console.log('after wait, before click');
  });
  browser.wait(()=> {
    console.log('isDisplayed');
    return $('#auto-refresh').isDisplayed();
  });
  $('#auto-refresh').click();
  browser.controlFlow().execute(function() {
    console.log('after click');
  });

  browser.wait(() => {
    return $('.status-box').isPresent().then((isPresent)=> {
      if(isPresent) {
        return hasClass($('.status-box'), 'status-done')
            .then((containsClass) => {
              if(!containsClass) {
                return browser.refresh().then(() => false);
              }
              return true;
            });
      }
      return browser.sleep(constants.BROWSER_WAIT_MS)
          .then(() => false);
    })
  });

  return browser.controlFlow().execute(function() {
    browser.ignoreSynchronization = false;
  });
}
  fit('mapreduce job is idempotent', function() {
    var subBuildingUrl = '/xyz/#all';
    var mapReduceJob = '/import/xyz/';
    helpers.waitForMapReduceJobToComplete(mapReduceJob);

    browser.get(subBuildingUrl);
    var tableView = new TableView();
    var originalCount = 0;
    tableView.count().then((cnt)=>{
      originalCount = cnt;
    });

    tableView.deleteRowNumber(1);
    browser.sleep(5000);
    browser.controlFlow().execute(function() {
      expect(tableView.count()).toBeLessThan(originalCount);
    });

    helpers.waitForMapReduceJobToComplete(mapReduceJob);
    browser.get(subBuildingUrl);
    browser.controlFlow().execute(function() {
      tableView = new TableView();
      expect(tableView.count()).toEqual(originalCount);
    });
  });

//رتب
يبدأ الاختبار على التطبيق الزاوي.
تبدأ وظيفة mapreduce على عنوان URL وليس تطبيق زاوية
يعود إلى التطبيق الزاوي لتسجيل عدد الصفوف

//فعل
يحذف 1 سجل
يتحقق من حدوث الحذف

// يجزم
تبدأ وظيفة mapreduce على عنوان URL وليس تطبيق زاوية
_fails_ في المرة الثانية قبل أن نتمكن من تأكيد وظيفة mapreduce تعيد إنشاء البيانات (نفس عدد الصفوف قبل الحذف).

فيما يلي تفاصيل السجل ذات الصلة:

wait
after wait, before click
isDisplayed
isDisplayed
after click
[17:48:09] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:24] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:40] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
[17:48:55] W/element - more than one element found for locator By(css selector, .status-box) - the first result will be used
wait
F

Failures:
  Message:
    Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined.  This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.  See http://git.io/v4gXM for details"
  Stack:

يفشل دائمًا في المرة الثانية من خلال waitForMapReduceJobToComplete في سجل "انتظار".

الحل موثق هنا: https://github.com/angular/protractor/issues/2787

داخل المتصفح. انتظر ، يجب عليك إعادة تعيين ignoreSynchronization. لقد فعلت هذا لجميع browser.waits حيث كان ignoreSynchronization صحيحًا بالفعل.

  browser.wait(()=> {
    browser.ignoreSynchronization = true;
    return $('#auto-refresh').isPresent();
  });

لقد نجح اختباري الآن.

هل لديك أي حل للانتظار باستخدام browser.ExpectedConditions؟

كيف سيبدو التصحيح إذا كنت ستستخدم هذا الرمز؟

browser.wait(EC.visibilityOf(element), timeout, message);?

شكر!

azachar يبدو أن هذا واحد يعمل:

browser.wait(() => {
  browser.ignoreSynchronization = true;
  return EC.visibilityOf(element);
}, timeout, message);

لا أعرف ما الذي يحدث ، لسبب ما في هذه الكتلة ، لا يزال ignoreSynchronization خاطئًا حتى تقوم بتعيينه بالقوة. تابعت في # 2787 بهذا.

تحديث: هناك شروط سباق إذا قمت بتعيين / إعادة تعيين ignoreSynchronization ، فمن الأفضل وضع ذلك في تدفق التحكم. انظر # 4061.

أحصل على هذا بدون ignoreSynchronization بشيء كهذا ....

afterAll(function(){
    adminPage.header.logout();
    // FIXME: WTF why isn't even this working 100% of the time
    setTimeout(function(){
      expect(loginPage.isVisible()).toBeTruthy();
    }, 1000 )
  })

يبدو أن تحميل loginPage يستغرق ثانية واحدة ولا تساعد هذه المهلة المحددة. يبدو أن browser.wait يساعد قليلاً ولكنه لا يزال يفشل.

tassoevan hi dude ، هل يمكنك إخباري بكيفية تكوين هذا المتصفح. انتظر () مثل وصفك ومكان تهيئته

(function (expectedUrl, timeout) {
    var loaded = false;

    browser.wait(function () {
        browser.executeScript(function () {
            return {
                url: window.location.href,
                haveAngular: !!window.angular
            };
        }).then(function (obj) {
            loaded = (obj.url == expectedUrl && obj.haveAngular);
        });

        return loaded;
    }, timeout);
})(expectedUrl, timeout);

في angular (2) - تأكد من أن المواصفات الخاصة بك تبدأ باستخدام w / browser.get ('/') ؛

import { browser, element, by } from 'protractor';

it('should display message saying app works', () => {
    browser.get('/');

    let title = element(by.id('title')).getText();
    expect<any>(title).toEqual('cool');
});

بعد النظر في الحل البديل الرائع لـ tassoevan والحصول على اختبارات إعادة التوجيه الخاصة بي ، أدركت أنني سأشارك حل TypeScript و Angular2. لقد وضعت هذه الوظيفة في فئة صفحة الملخص ، لكنني أعتقد أنها قد تتناسب بشكل أفضل مع الكائن browser .

/**
  Wait for the page to make it to the next URL before continuing.
  A supplement to browser.waitForAngular() because protractor will continue control
  flow before navigation starts.

  Inputs:
    necessaryUrlFragment: a portion of the url that signifies you have successfully navigated
    timeout: the number of ms to wait before throwing an error. Defaults to the browsers default
      page timeout.
 */
waitForRedirect(necessaryUrlFragment: string, timeout: number = browser.getPageTimeout) {
  // Before we tell the browser to wait, assume it has not navigated
  let hasRedirected = false;

  // Passing a function to browser.wait() tells protractor to call that function repeatedly.
  // This function returns the closure variable hasRedirected, which will be set to true once the
  // necessaryUrlFragment has been found in the url
  browser.wait(() => {
    browser.getCurrentUrl()
      // Check to see if necessaryUrlFragment is in the current url
      .then(url => url.includes(necessaryUrlFragment))
      // Update our navigation status
      .then(hasNavigated => {
        hasRedirected = hasNavigated;
      });

    // Return our navigation status every time protractor asks for it - even if navigation is
    // not complete
    return hasRedirected;
  }, timeout);
}

فيما يلي مثال على كيفية استخدامها في الاختبار:

it('Should be able load the preferences page', () => {
  page.navigateToMarketing();
  page.clickButton('Manage Preferences');
  page.waitForRedirect('preferences/preferences');
  expect(page.getPageTitle()).toBe('MY PREFERENCES');
});

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

وصف ("تسجيل الدخول إلى التطبيق" ، الوظيفة () {
var userID = element (by.name ('username')) ؛
var password = element (by.name ('password')) ؛
var logInButton = element (by.id ('إرسال')) ؛

function loginToApplication (سلسلة SSOID ، stringPassword) {
userID.sendKeys (stringSSOID) ،
password.sendKeys (stringPassword) ،
logInButton.click () ،

}

beforeEach (الوظيفة () {
browser.ignoreSynchronisation = صحيح ؛
browser.get ('') ؛ // الذي سينتقل إلى صفحة تسجيل الدخول للتطبيق
}) ؛

it ('have login to application'، function () {
loginToApplication ("77777" ، "اختبار")
browser.ignoreSynchronisation = خطأ
توقع (browser.getTitle ()). toEqual ("البرنامج") ؛
}) ؛
}) ؛

الحصول على رسالة الخطأ أدناه:
فشل: حدث خطأ أثناء انتظار مزامنة المنقلة مع الصفحة: "window.an
gular غير محدد. يمكن أن يكون هذا إما لأن هذه صفحة غير زاويّة أو
لأن اختبارك يتضمن التنقل من جانب العميل ، والذي يمكن أن يتداخل مع Prot
ractor bootstrapping. راجع http://git.io/v4gXM للحصول على التفاصيل "

مرحبًا ألق نظرة على http://www.protractortest.org/#/timeouts # how-to-disable-waiting-for-angular
هناك طريقتان لتسجيل الدخول باستخدام صفحة غير Angular ولكن أولاً جرب هذا العرض في المستندات.
تحياتي الحارة
جمل

أدرك أنها مناقشة مطولة بالفعل ، لكن هذا قد يساعد شخصًا ما.

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

لذلك ربما يمكن القيام بشيء ما فيما يتعلق برسالة الخطأ ، نظرًا لأن الحالة المذكورة أعلاه ليست "التنقل من جانب العميل" في حد ذاتها :)

مرحبًا ArnaudPel

Tnx للمعلومات الإضافية الخاصة بك. ربما يمكنك إضافة العلاقات العامة لشرح ذلك في المستندات إذا كنت تريد؟

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

Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined.  This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.  See http://git.io/v4gXM for details"

لا يبدو أن أداء خدعة ignoreSync يساعد:

browser.ignoreSynchronization = true;
// do work here with browser.driver 
browser.ignoreSynchronization = false;

وصل الرابط هنا في هذا الموضوع ، ولكن ما يجب فعله بعد ذلك ليس واضحًا تمامًا.

تم الاختبار مع إصدارات منقلة: 5.2.1 ، 5.1.1 ، 5.1.0 ، 5.0.0

benjaminapetersen - يمكنك تجربة استخدام waitForAngularEnabled بدلاً من ignoreSynchronization . إنني أنظر إلى الصفحة المرجعية لواجهة برمجة التطبيقات الخاصة بـ Protractor v5.1.2 ولم تعد تتضمن ignoreSynchronization ، لكن بالنظر إلى CHANGELOG لا أرى متى ، إذا تم إهمال هذه الخاصية رسميًا ...

حاولت waitForAngularEnabled لكنني ما زلت أحصل على نفس الخطأ الذي يحصل عليه benjaminapetersen . هنا هو التكوين الحالي الخاص بي:

exports.config = {
    rootElement: 'app',
    baseUrl: 'http://localhost:1384/#/',
    framework: 'jasmine',
    seleniumAddress: 'http://localhost:4444/wd/hub',
    specs: ['./tests/e2e/*.js'],
    multiCapabilities: [{
      'browserName': 'firefox',
      //'browserName': 'chrome',
    }, 
  ],
  jasmineNodeOpts: {
    showColors: true
  },
}

//example test: 

describe('Overall navigation', function () {
  it('test link', function () {
     browser.get('http://localhost:1384/#/registrerprodukt');
  });
});

tompsnm شكرا! على الرغم من أنه يبدو أن waitForAngularEnabled أيضًا مهمل ....

benjaminapetersen أن علامة المستند الموقوفة موجودة على خاصية ignoreSynchronization ، وليس waitForAngularEnabled

waitForAngularEnabled لا يعمل معي أيضًا. لقد عملت مع IgnoreSynchronization قبل الترقية إلى منقلة 5. أحتاج إلى التبديل إلى إطار iframe غير الزاوي ، والآن لا يمكنني ذلك. يرجى تقديم النصيحة. شكر

@ seel93 حاول التعليق على rootElement: 'app' ، أو حاول استبداله بـ rootElement: '*[ng-app]' .
لقد قمت للتو بتثبيت أحدث إصدار من المنقلة وكتبت بعض المواصفات لتطبيق Angular 1.6.1 ويبدو أنه يعمل بشكل جيد حتى الآن. لقد واجهت رسالة الخطأ هذه عندما كنت أحاول تسجيل الدخول في تطبيقي ولكني الآن أستخدم هذا الأسلوب:

onPrepare: function () {
    browser.driver.manage().timeouts().setScriptTimeout(60000);

    browser.driver.get('http://app.local.dev'+ '/login');
    browser.driver.findElement(by.id('user_login')).sendKeys('d','e','m', 'o');
    browser.driver.findElement(by.id('user_pass')).sendKeys('d','e','m', 'o');
    browser.driver.findElement(by.id('submit')).click();

    // Login takes some time, so wait until it's done.
    return browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
            return /students\/agenda/.test(url);
        });
    }, 30000);
}

ملاحظة: لدي ng-app مضبوطًا على علامة html

rootElement: '*[ng-app]' ؟ يعمل فقط على البحث في الصفحة عن أي عقدة؟ لم أر هذا من قبل.

لقد حللت مشكلتي للتو. سبب الجذر: الآثار الجانبية لوحدة تحكم المطور

كنت أواجه هذه المشكلة وانتهى بي الأمر بالتغيير إلى المنقلة "^ 5.3.2" في الحزمة الخاصة بي. json

لدي صفحة تسجيل دخول عادية ثم أعد توجيهي إلى الصفحة الزاوية.

لذلك لا أعرف بالضبط كيف أو لماذا يعمل ، لأنني جديد حقًا على e2e الزاوي والزاوي ، لكنني عملت عليه بمجرد استخدام browser.waitForAngularEnabled(false); مرتين.

logIn() {
    const email = 'test';
    const password = 'test';

    browser.waitForAngularEnabled(false);
    browser.get('/login');

    element(by.id('username')).sendKeys(email);
    element(by.id('password')).sendKeys(password);
    element(by.css('[type="submit"]')).click();

    return browser.wait(function() {
        browser.waitForAngularEnabled(false);
        return browser.getCurrentUrl().then(function(url) {
            browser.waitForAngularEnabled(true);
            return /dashboard/.test(url);
        });
    }, 5000);
}

آمل أن يساعد.

يكاد هذا الحل الأخير يعمل معي إلا إذا تلقيت خطأ بعد اجتياز الاختبار:
خطأ: ECONNREFUSED connect ECONNREFUSED 127.0.0.1:4444 من: المهمة: WebDriver.getCurrentUrl ()

لقد حصلت على هذا الخطأ عن طريق عمل العنصر (). getText () خارج المواصفات (بدون حدوث browser.get ()) - كان يحاول إنشاء اختصار لإخراج div معين خارج المواصفات. حولته إلى وظيفة وكان كل شيء على ما يرام.

إنه يعمل لأي شيء ، لا يمكنك استخدام أكثر من 'عنصر (). anyfunction ()' خارج 'it'.
اكتشفها بالطريقة الصعبة ...

حدث خطأ عند التبديل من صفحة ويب إلى أخرى في منقلة:

فشل: خطأ أثناء انتظار منقلة للمزامنة مع الصفحة: "كل من قابلية اختبار angularJS وقابلية الاختبار الزاوي غير معرَّفين. قد يكون هذا إما لأن هذه صفحة غير ذات زاوية أو لأن اختبارك يتضمن تنقلًا من جانب العميل ، والذي يمكن أن يتداخل مع منقلة bootstrapping. راجع http://git.io/v4gXM للحصول على التفاصيل "

الكود هو:
var url = browser.params.login.Url ؛
browser.get (url + '/ #! / app / إدارة المستخدمين) ؛

لقد بدأت في الحصول على هذا الخطأ فجأة يبني التصوير المقطعي. لقد لاحظت أن هذا الخطأ يحدث فقط مع Firefox مقطوعة الرأس وعمل جيدًا مع Chrome بدون رأس على Linux. لقد قمت بالترقية من Firefox v69 إلى الإصدار الأحدث (v72.0.2 في الوقت الحالي) والذي أدى إلى حل هذه المشكلة.

تم إهمال browser.ignoreSynchronization ويجب عدم استخدامه. كما تم التلميح من المنشور الأصلي ، يبدو أن استخدام browser.get يحل المشكلة.

إذا كنت تريد استخدامه بطريقة يمكنك من خلالها تعطيل وتمكين waitForAngularEnabled تشاء ، فأنت بحاجة إلى استخدام browser.get بعد تعيين waitForAngularEnabled(true) . فمثلا:

// do things on your Angular application

waitForAngularEnabled(false)

// do things on non-angular page

waitForAngularEnabled(true)
browser.get('/home') // this is a page from your Angular application

كتل browser.get حتى يتم تحميل الصفحة Angular.

أواجه هذا الخطأ حاليًا:

_ حدث استثناء: خطأ: خطأ أثناء انتظار المنقلة للمزامنة مع الصفحة: "كل من قابلية اختبار angularJS وقابلية الاختبار الزاوي غير معرَّفين. قد يكون هذا إما لأن هذه صفحة غير زاوية أو لأن اختبارك يتضمن التنقل من جانب العميل ، والذي يمكن أن تتداخل مع عملية إقلاع منقلة. راجع http://git.io/v4gXM للحصول على التفاصيل "_

ولكن فقط عند محاولة التصحيح من خلال إما مصحح أخطاء VS Code أو مصحح chrome inspector (اتبع الإرشادات هنا لكلتا الحالتين https://www.protractortest.org/#/debugging).

هذا هو التهيئة الخاصة بي launch.json في VSCode.

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "E2E Test",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\node_modules\\protractor\\bin\\protractor",
            "args": ["${workspaceFolder}\\e2e\\protractor.conf.js"],
            "outFiles": [
                "${workspaceFolder}/e2e/**/*.js",
                "${workspaceFolder}/src/**/*.js"
            ]
        }
    ]
}

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

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