/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview
 * @suppress {missingRequire} Swapping using fully qualified name
 */

goog.module('goog.i18n.RelativeDateTimeFormatTest');
goog.setTestOnly('goog.i18n.RelativeDateTimeFormatTest');

const LocaleFeature = goog.require('goog.i18n.LocaleFeature');
const NumberFormatSymbols_ar_EG = goog.require('goog.i18n.NumberFormatSymbols_ar_EG');
const NumberFormatSymbols_en = goog.require('goog.i18n.NumberFormatSymbols_en');
const NumberFormatSymbols_es = goog.require('goog.i18n.NumberFormatSymbols_es');
const NumberFormatSymbols_fa = goog.require('goog.i18n.NumberFormatSymbols_fa');
const PropertyReplacer = goog.require('goog.testing.PropertyReplacer');
const RelativeDateTimeFormat = goog.require('goog.i18n.RelativeDateTimeFormat');
const assertI18n = goog.require('goog.testing.i18n.asserts');
const relativeDateTimeSymbols = goog.require('goog.i18n.relativeDateTimeSymbols');
const relativeDateTimeSymbolsExt = goog.require('goog.i18n.relativeDateTimeSymbolsExt');
const testSuite = goog.require('goog.testing.testSuite');

/** @suppress {visibility} suppression added to enable type checking */
const Plurals_en = goog.i18n.pluralRules.enSelect_;
/** @suppress {visibility} suppression added to enable type checking */
const Plurals_he = goog.i18n.pluralRules.heSelect_;
/** @suppress {visibility} suppression added to enable type checking */
const Plurals_ar = goog.i18n.pluralRules.arSelect_;

// For changing values in test
let propertyReplacer;

/** @unrestricted */
const DirectionData = class {
  /**
   * @param {string} locale
   * @param {number} style
   * @param {number} direction
   * @param {number} unit
   * @param {string} expected
   * @param {string|undefined} pluralrules
   */
  constructor(locale, style, direction, unit, expected, pluralrules) {
    this.locale = locale;
    this.style = style;
    this.direction = direction;
    this.unit = unit;
    this.expected = expected;
    this.pluralrules = pluralrules;
  }

  /** @return {string} Error description. */
  getErrorDescription() {
    return 'Error for locale:' + this.locale + ' style: ' + this.style +
        ' quantity =' + this.direction + ' unit =' + this.unit +
        ' pluralrules = ' + this.pluralrules + '\'';
  }
};

// Add alternative results for assertI18nEquals.
// These come from ICU72 / CLDR42 updates.
assertI18n.addI18nMapping('۳روزدیگر', '۳روزبعد');
assertI18n.addI18nMapping('in 0 wk.', 'in 0w');
assertI18n.addI18nMapping('0 wk. ago', '0w ago');
assertI18n.addI18nMapping('2d ago', '2 days ago');
assertI18n.addI18nMapping('in 5 wk.', 'in 5w');
assertI18n.addI18nMapping('5 wk. ago', '5 w ago');
assertI18n.addI18nMapping('in 2w', 'in 2wk.');


/** @const {!Object<string, !Object>} */
const localeSymbols = {
  'en': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_en,
  },
  'es': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_es,
  },
  'fr': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_fr,
  },
  'ar': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_ar,
  },
  'ar_EG': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_ar_EG,
  },
  'ar_LY': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbolsExt.RelativeDateTimeSymbols_ar_LY,
  },
  'agq': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbolsExt.RelativeDateTimeSymbols_agq,
  },
  'ar_AE': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbolsExt.RelativeDateTimeSymbols_ar_AE,
  },
  'as': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbolsExt.RelativeDateTimeSymbols_as,
  },
  'fa': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_fa,
  },
  'he': {
    RelativeDateTimeFormatSymbols:
        relativeDateTimeSymbols.RelativeDateTimeSymbols_he,
  },
};

// clang-format off
/** @suppress {checkTypes} suppression added to enable type checking */
const formatDirectionTestData = [
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, 'yesterday'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.DAY, 'today'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.DAY, 'tomorrow'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.DAY, 'in 2 days'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.DAY, '2 days ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, -2, RelativeDateTimeFormat.Unit.DAY, '2 days ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, -2, RelativeDateTimeFormat.Unit.DAY, '2d ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.WEEK, '2 weeks ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.WEEK, '2 weeks ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.WEEK, 'last week'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.WEEK, 'this week'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.WEEK, 'next week'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.WEEK, 'next week'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.WEEK, 'in 2 weeks'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -3, RelativeDateTimeFormat.Unit.WEEK, '3 weeks ago'),
 new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, 5, RelativeDateTimeFormat.Unit.WEEK, 'in 5 wk.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, -5, RelativeDateTimeFormat.Unit.WEEK, '5 wk. ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.SECOND, 'in 1 second'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.SECOND, 'now'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.SECOND, 'now'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.SECOND, '1 second ago'),

  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.MINUTE, 'in 1 minute'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.MINUTE, '1 minute ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.HOUR, 'in 1 hour'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.HOUR, '1 hour ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.MONTH, 'next month'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.QUARTER, 'next quarter'),
  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.QUARTER, 'next qtr.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.YEAR, 'in 2 years'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.WEEK, 'next week'),
  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.WEEK, 'next wk.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, 1, RelativeDateTimeFormat.Unit.WEEK, 'next wk.'),

  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.WEEK, 'الأسبوع القادم', Plurals_ar),

  /* Trying with locale "es" */
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, 'ayer'),
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.DAY, 'hoy'),
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.DAY, 'mañana'),
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.DAY, 'pasado mañana'),
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.DAY, 'anteayer'),
  new DirectionData('es', RelativeDateTimeFormat.Style.SHORT, -2, RelativeDateTimeFormat.Unit.DAY, 'anteayer'),
  new DirectionData('es', RelativeDateTimeFormat.Style.NARROW, -2, RelativeDateTimeFormat.Unit.DAY, 'anteayer'),

  new DirectionData('fr', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.SECOND, 'il y a 1 seconde'),
  new DirectionData('fr', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.SECOND, 'maintenant')
];

// TODO(user): re-examine when ICU4J and CLDR data are updated.
/** @suppress {checkTypes} suppression added to enable type checking */
const forcedNumericTestData = [
  // Special cases for MINUTE and HOUR, and SECOND != 0, forced numeric mode.
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0.0, RelativeDateTimeFormat.Unit.YEAR, 'in 0 years'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.SECOND, 'in 0 seconds'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, 1, RelativeDateTimeFormat.Unit.MINUTE, 'in 1 minute'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, '1 day ago'),
  new DirectionData(
      'en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.HOUR, 'in 0 hours'),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatNumericTestData = [
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 7, RelativeDateTimeFormat.Unit.DAY, 'in 7 days'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.DAY, '2 days ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1.5, RelativeDateTimeFormat.Unit.DAY, '1.5 days ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, '1 day ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.DAY, 'in 0 days'),
  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 2, RelativeDateTimeFormat.Unit.HOUR, 'in 2 hr.'),

  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 0, RelativeDateTimeFormat.Unit.HOUR, 'in 0 hr.'),  // Not "this hour"

    new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.SECOND, 'in 0 seconds'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.SECOND, '0 seconds ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.DAY, 'in 2 days'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 2.5, RelativeDateTimeFormat.Unit.DAY, 'in 2.5 days'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0.5, RelativeDateTimeFormat.Unit.DAY, 'in 0.5 days'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -2, RelativeDateTimeFormat.Unit.WEEK, '2 weeks ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.WEEK, '1 week ago'),
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.WEEK, 'in 0 weeks'),

  // Test with negative zero
  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.WEEK, '0 weeks ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.WEEK, 'in 1 wk.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.SHORT, 6, RelativeDateTimeFormat.Unit.WEEK, 'in 6 wk.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, 2, RelativeDateTimeFormat.Unit.WEEK, 'in 2w'),
  // TODO: Lots more needed.

  new DirectionData('fr', RelativeDateTimeFormat.Style.NARROW, 2, RelativeDateTimeFormat.Unit.SECOND, '+2 s'),

  new DirectionData('fr', RelativeDateTimeFormat.Style.NARROW, 2, RelativeDateTimeFormat.Unit.SECOND, '+2 s'),
  new DirectionData('fr', RelativeDateTimeFormat.Style.LONG, 2, RelativeDateTimeFormat.Unit.SECOND, 'dans 2 secondes'),
  new DirectionData('fr', RelativeDateTimeFormat.Style.SHORT, 2, RelativeDateTimeFormat.Unit.SECOND, 'dans 2 s'),

  // Test signed zero
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, 0, RelativeDateTimeFormat.Unit.WEEK, 'in 0 wk.'),
  new DirectionData('en', RelativeDateTimeFormat.Style.NARROW, -0, RelativeDateTimeFormat.Unit.WEEK, '0 wk. ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.DAY, '0 days ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.MONTH, '0 months ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.QUARTER, '0 quarters ago'),

  new DirectionData('en', RelativeDateTimeFormat.Style.LONG, -0, RelativeDateTimeFormat.Unit.YEAR, '0 years ago'),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatFarsiData = [
  // Other locales, too!
  new DirectionData('fa', RelativeDateTimeFormat.Style.NARROW, 3, RelativeDateTimeFormat.Unit.DAY, '۳ روز بعد'),
  new DirectionData('fa', RelativeDateTimeFormat.Style.LONG, 3, RelativeDateTimeFormat.Unit.DAY, '۳ روز دیگر'),
  new DirectionData('fa', RelativeDateTimeFormat.Style.SHORT, -3, RelativeDateTimeFormat.Unit.MONTH, '۳ ماه پیش'),
  new DirectionData('fa', RelativeDateTimeFormat.Style.SHORT, -17, RelativeDateTimeFormat.Unit.HOUR, '۱۷ ساعت پیش'),
  new DirectionData('fa', RelativeDateTimeFormat.Style.SHORT, 9, RelativeDateTimeFormat.Unit.SECOND, '۹ ثانیه بعد'),
  new DirectionData('fa', RelativeDateTimeFormat.Style.SHORT, -11, RelativeDateTimeFormat.Unit.WEEK, '۱۱ هفته پیش'),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatArEgData = [
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.LONG, 0, RelativeDateTimeFormat.Unit.DAY, 'خلال ٠ يوم', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 0, RelativeDateTimeFormat.Unit.DAY, 'خلال ٠ يوم', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.MONTH, 'خلال شهر واحد', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, -1, RelativeDateTimeFormat.Unit.DAY, 'قبل يوم واحد', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 2, RelativeDateTimeFormat.Unit.DAY, 'خلال يومين', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 3, RelativeDateTimeFormat.Unit.HOUR, 'خلال ٣ ساعات', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 28, RelativeDateTimeFormat.Unit.SECOND, 'خلال ٢٨ ثانية', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 101, RelativeDateTimeFormat.Unit.WEEK, 'خلال ١٠١ أسبوع', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.SHORT, 1.5, RelativeDateTimeFormat.Unit.YEAR, 'خلال ١٫٥ سنة', Plurals_ar),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatNumericSpanishData = [
  new DirectionData('es', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, 'hace 1 día'),
  new DirectionData('es', RelativeDateTimeFormat.Style.SHORT, -2, RelativeDateTimeFormat.Unit.DAY, ['hace 2 d', 'hace 2 días']),  // CLDR 40 added new short form
  new DirectionData('es', RelativeDateTimeFormat.Style.SHORT, 3, RelativeDateTimeFormat.Unit.DAY, ['dentro de 3 d', 'dentro de 3 días']),  // CLDR 40 added new short form
];


/** @suppress {checkTypes} suppression added to enable type checking */
const formatNumericExtendedData = [
  new DirectionData('ar_AE', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'قبل يومين', Plurals_ar),
  new DirectionData('ar_EG', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'قبل يومين', Plurals_ar),
  new DirectionData('agq', RelativeDateTimeFormat.Style.LONG, -1, RelativeDateTimeFormat.Unit.DAY, '-1 d'),
  new DirectionData('agq', RelativeDateTimeFormat.Style.SHORT, 2, RelativeDateTimeFormat.Unit.DAY, '+2 d'),
  new DirectionData('as', RelativeDateTimeFormat.Style.SHORT, 1, RelativeDateTimeFormat.Unit.DAY, '1 দিনত'),
  new DirectionData('as', RelativeDateTimeFormat.Style.SHORT, 3, RelativeDateTimeFormat.Unit.DAY, '3 দিনত'),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatNumericRtlData = [
  new DirectionData('he', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'לפני יומיים', Plurals_he),
  // Should this be 'לפני יומיים'?
  new DirectionData('he', RelativeDateTimeFormat.Style.LONG, 2,
                    RelativeDateTimeFormat.Unit.DAY, 'בעוד יומיים', Plurals_he),
  // Should this be 'בעוד יומיים'?
  new DirectionData('he', RelativeDateTimeFormat.Style.LONG, -3,
                    RelativeDateTimeFormat.Unit.DAY, 'לפני 3 ימים', Plurals_he),
  new DirectionData('ar', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'قبل يومين', Plurals_ar),
  new DirectionData('ar', RelativeDateTimeFormat.Style.LONG, 2,
                    RelativeDateTimeFormat.Unit.DAY, 'خلال يومين', Plurals_ar),
];

/** @suppress {checkTypes} suppression added to enable type checking */
const formatAutoRtlData = [
  new DirectionData('he', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'שלשום', Plurals_he),
  new DirectionData('he', RelativeDateTimeFormat.Style.LONG, -3,
                    RelativeDateTimeFormat.Unit.DAY, 'לפני 3 ימים', Plurals_he),
  new DirectionData('ar', RelativeDateTimeFormat.Style.LONG, -2,
                    RelativeDateTimeFormat.Unit.DAY, 'أول أمس', Plurals_ar),
];

// clang-format on



// Tests both JavaScript and ECMAScript on supporting browsers.
// Sets up goog.USE_ECMASCRIPT_I18N_RDTF flag in each function.
let testECMAScriptOptions = [false];
const rdtf = new RelativeDateTimeFormat();
if (rdtf.hasNativeRdtf()) {
  // Add test if the browser environment supports ECMAScript implementation.
  testECMAScriptOptions.push(true);
}

testSuite({
  getTestName: function() {
    return 'RelativeDateTimeFormat Tests';
  },

  setUpPage() {
    propertyReplacer = new PropertyReplacer();
  },

  setUp: function() {
    // Use computed properties to avoid compiler checks of defines.
    goog['LOCALE'] = 'en';
    /**
     * @suppress {constantProperty} suppression added to enable type checking
     */
    goog.i18n.NumberFormatSymbols = NumberFormatSymbols_en;
    goog.i18n.pluralRules.select = Plurals_en;
    propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', false);
  },

  tearDown: function() {
    /**
     * @suppress {constantProperty} suppression added to enable type checking
     */
    goog.i18n.NumberFormatSymbols = NumberFormatSymbols_en;
    // Use computed properties to avoid compiler checks of defines.
    goog['LOCALE'] = 'en';
  },

  // Test with style, but no number formatting.
  testFormatStyle: function() {
    // Try with both JavaScript and ECMAScript implementations, if present.
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      for (let i = 0; i < formatDirectionTestData.length; i++) {
        const data = formatDirectionTestData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;

        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.AUTO, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            'TestFormatStyle index ' + i + ': ' + data.getErrorDescription(),
            data.expected, result);
      }
    }
  },

  testFormatNumericStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      /**
       * @suppress {constantProperty} suppression added to enable type checking
       */
      goog.i18n.NumberFormatSymbols = NumberFormatSymbols_en;
      for (let i = 0; i < formatNumericTestData.length; i++) {
        const data = formatNumericTestData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmtAlways = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmtAlways.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);

        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmtUndefined = new RelativeDateTimeFormat(
            undefined, data.style, symbols.RelativeDateTimeFormatSymbols);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected,
            fmtUndefined.format(data.direction, data.unit));
      }
    }
  },

  testNumericMode: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      /**
       * @suppress {constantProperty} suppression added to enable type checking
       */
      goog.i18n.NumberFormatSymbols = NumberFormatSymbols_es;
      // Use computed properties to avoid compiler checks of defines.
      goog['LOCALE'] = 'es';
      /** @suppress {checkTypes} suppression added to enable type checking */
      const data = new DirectionData(
          'es', RelativeDateTimeFormat.Style.LONG, -1,
          RelativeDateTimeFormat.Unit.DAY, 'ayer');

      const symbols = localeSymbols[data.locale];
      /**
       * @suppress {checkTypes,strictMissingProperties} suppression added to
       * enable type checking
       */
      const fmt = new RelativeDateTimeFormat(
          RelativeDateTimeFormat.NumericOption.AUTO, data.style,
          symbols.RelativeDateTimeFormatSymbols);

      let numMode = fmt.getNumericMode();
      assertEquals(
          data.getErrorDescription(), numMode,
          RelativeDateTimeFormat.NumericOption.AUTO);
      /** @suppress {checkTypes} suppression added to enable type checking */
      const result = fmt.format(data.direction, data.unit);
      assertEquals(data.getErrorDescription(), data.expected, result);

      // Try with forced numeric mode.
      /**
       * @suppress {checkTypes,strictMissingProperties} suppression added to
       * enable type checking
       */
      const fmtNumericOnly = new RelativeDateTimeFormat(
          RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
          symbols.RelativeDateTimeFormatSymbols);
      numMode = fmtNumericOnly.getNumericMode();
      assertEquals(
          data.getErrorDescription(), numMode,
          RelativeDateTimeFormat.NumericOption.ALWAYS);
      /** @suppress {checkTypes} suppression added to enable type checking */
      const numResult = fmtNumericOnly.format(data.direction, data.unit);
      assertEquals(data.getErrorDescription(), 'hace 1 día', numResult);
    }
  },

  testFormatNumericSpanishStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      /**
       * @suppress {constantProperty} suppression added to enable type checking
       */
      goog.i18n.NumberFormatSymbols = NumberFormatSymbols_es;
      // Use computed properties to avoid compiler checks of defines.
      goog['LOCALE'] = 'es';
      for (let i = 0; i < formatNumericSpanishData.length; i++) {
        const data = formatNumericSpanishData[i];
        const symbols = localeSymbols[data.locale];
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        if (Array.isArray(data.expected)) {
          // Expected data is one of the array elements. Needed for
          // browsers out of sync with CLDR updates.
          assertTrue(
              data.getErrorDescription(), data.expected.includes(result));
        } else {
          assertEquals(data.getErrorDescription(), data.expected, result);
        }
      }
    }
  },

  testFormatNumericFarsiStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      for (let i = 0; i < formatFarsiData.length; i++) {
        const data = formatFarsiData[i];
        const symbols = localeSymbols[data.locale];
        /**
         * @suppress {constantProperty} suppression added to enable type
         * checking
         */
        goog.i18n.NumberFormatSymbols = NumberFormatSymbols_fa;

        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;

        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);
      }
    }
  },

  testFormatNumericArEgStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      for (let i = 0; i < formatArEgData.length; i++) {
        const data = formatArEgData[i];
        const symbols = localeSymbols[data.locale];
        /**
         * @suppress {constantProperty} suppression added to enable type
         * checking
         */
        goog.i18n.NumberFormatSymbols = NumberFormatSymbols_ar_EG;

        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;
        goog.i18n.pluralRules.select = data.pluralrules;
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);
      }
    }
  },

  testFormatNumericExtendedStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      for (let i = 0; i < formatNumericExtendedData.length; i++) {
        const data = formatNumericExtendedData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        if (data.pluralrules) {
          goog.i18n.pluralRules.select = data.pluralrules;
        } else {
          goog.i18n.pluralRules.select = Plurals_en;
        }

        /* Only test ECMAScript mode if locale data is expected */
        if (!fmt.isNativeMode()) {
          const result = fmt.format(data.direction, data.unit);
          assertI18n.assertI18nEquals(
              data.getErrorDescription(), data.expected, result);
        }
      }
    }
  },

  testForcedNumeric: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      /**
       * @suppress {constantProperty} suppression added to enable type checking
       */
      goog.i18n.NumberFormatSymbols = NumberFormatSymbols_en;
      for (let i = 0; i < forcedNumericTestData.length; i++) {
        const data = forcedNumericTestData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);
      }
    }
  },

  testFormatNumericRtl: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      /**
       * @suppress {constantProperty} suppression added to enable type checking
       */
      goog.i18n.NumberFormatSymbols = NumberFormatSymbols_en;
      for (let i = 0; i < formatNumericRtlData.length; i++) {
        const data = formatNumericRtlData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;

        // Explicitly set plural rules to get correct option.
        goog.i18n.pluralRules.select = data.pluralrules;

        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        let fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.ALWAYS, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        let result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);
      }
    }
  },

  testFormatAutoRtl: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      for (let i = 0; i < formatAutoRtlData.length; i++) {
        const data = formatAutoRtlData[i];
        const symbols = localeSymbols[data.locale];
        // Use computed properties to avoid compiler checks of defines.
        goog['LOCALE'] = data.locale;
        goog.i18n.pluralRules.select = data.pluralrules;
        /**
         * @suppress {strictMissingProperties} suppression added to enable type
         * checking
         */
        const fmt = new RelativeDateTimeFormat(
            RelativeDateTimeFormat.NumericOption.AUTO, data.style,
            symbols.RelativeDateTimeFormatSymbols);

        const result = fmt.format(data.direction, data.unit);
        assertI18n.assertI18nEquals(
            data.getErrorDescription(), data.expected, result);
      }
    }
  },

  // Test that retrieving style works.
  testGetStyle: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      let fmt = new RelativeDateTimeFormat();
      let style = fmt.getFormatStyle();
      assertEquals(
          'bad style returned', RelativeDateTimeFormat.Style.LONG, style);

      fmt = new RelativeDateTimeFormat(
          undefined, RelativeDateTimeFormat.Style.LONG);
      style = fmt.getFormatStyle();
      assertEquals(
          'bad style returned', RelativeDateTimeFormat.Style.LONG, style);

      fmt = new RelativeDateTimeFormat(
          undefined, RelativeDateTimeFormat.Style.SHORT);
      style = fmt.getFormatStyle();
      assertEquals(
          'bad style returned', RelativeDateTimeFormat.Style.SHORT, style);

      fmt = new RelativeDateTimeFormat(
          undefined, RelativeDateTimeFormat.Style.NARROW);
      style = fmt.getFormatStyle();
      assertEquals(
          'bad style returned', RelativeDateTimeFormat.Style.NARROW, style);
    }
  },

  // Test that retrieving relative unit is returned when defined only.
  testGetRelativeStringDefined: function() {
    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      let fmt = new RelativeDateTimeFormat();

      if (!fmt.isNativeMode()) {
        // These are only applicable for JavaScript implementation.
        let result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.DAY, -7);
        assertUndefined(result);  // Expect undefined for Day -7

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.DAY, -2);
        assertUndefined(result);  // Expect undefined for Day -2 English

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.YEAR, -1);
        assertEquals('last year', result);

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.YEAR, +1);
        assertEquals('next year', result);

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.MONTH, -1);
        assertEquals('last month', result);

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.MONTH, +1);
        assertEquals('next month', result);

        result = fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.DAY, 0);
        assertEquals('today', result);

        result =
            fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.QUARTER, 1);
        assertEquals('next quarter', result);

        result = fmt.isOffsetDefinedForUnit(RelativeDateTimeFormat.Unit.DAY, 2);
        assertUndefined(result);  // No special term for in 2 days in English
      }
    }
  },

  testEnShort: function() {
    // Use computed properties to avoid compiler checks of defines.
    goog['LOCALE'] = 'en';

    for (const val of testECMAScriptOptions) {
      propertyReplacer.replace(LocaleFeature, 'USE_ECMASCRIPT_I18N_RDTF', val);

      let fmt = new RelativeDateTimeFormat(
          RelativeDateTimeFormat.NumericOption.ALWAYS,
          RelativeDateTimeFormat.Style.SHORT);
      let result = fmt.format(2, RelativeDateTimeFormat.Unit.HOUR);
      assertEquals('in 2 hr.', result);

      result = fmt.format(1, RelativeDateTimeFormat.Unit.QUARTER);
      assertEquals('in 1 qtr.', result);

      const fmtAuto = new RelativeDateTimeFormat(
          RelativeDateTimeFormat.NumericOption.AUTO,
          RelativeDateTimeFormat.Style.SHORT);
      /** @suppress {checkVars} suppression added to enable type checking */
      result = fmtAuto.format(2, RelativeDateTimeFormat.Unit.HOUR);
      assertEquals('in 2 hr.', result);

      result = fmtAuto.format(1, RelativeDateTimeFormat.Unit.QUARTER);
      assertEquals('next qtr.', result);
    }
  },
});
