// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-string-replaceall --allow-natives-syntax

assertEquals('a-b-c-d', 'a+b+c+d'.replaceAll('+', '-'));
assertEquals('aaaa', 'abcd'.replaceAll(/./g, 'a'));
assertEquals('', ''.replaceAll('a', 'b'));
assertEquals('b', ''.replaceAll('', 'b'));
assertEquals('_x_x_x_', 'xxx'.replaceAll('', '_'));
assertEquals('yx', 'xxx'.replaceAll('xx', 'y'));
assertEquals('xxxx', 'xx'.replaceAll('xx', '$&$&'));
assertEquals('ii', '.+*$.+*$'.replaceAll('.+*$', 'i'));

{
  // Non regexp search value with replace method.
  const nonRegExpSearchValue = {
    [Symbol.replace]: (string, replacer) => {
      assertEquals(string, 'barbar');
      assertEquals(replacer, 'moo');
      return 'foo'
    },
    toString: () => {
      // Verify toString is not called.
      unreachable();
    }
  };
  assertEquals('foo', 'barbar'.replaceAll(nonRegExpSearchValue, 'moo'));
}

{
  // A custom regexp with non coercible flags.
  class RegExpNonCoercibleFlags extends RegExp {
    constructor() {
      super();
    }

    static get [Symbol.species]() {
      return RegExp;
    }

    get flags() { return null; }
  };
  assertThrows(
      () => { assertEquals(
          'foo',
          'barbar'.replaceAll(new RegExpNonCoercibleFlags, 'moo')); },
      TypeError);
}

{
  // Non regexp search value with replace property
  const nonRegExpSearchValue = {
    [Symbol.replace]: "doh",
    toString: () => {
      // Verify toString is not called.
      unreachable();
    }
  };
  assertThrows(
      () => { 'barbar'.replaceAll(nonRegExpSearchValue, 'moo'); },
      TypeError);
}

{
  // Non callable, non string replace value.
  const nonCallableNonStringReplace = {
    toString: () => {
      return 'boo';
    },
  };
  assertEquals('booboo', 'moomoo'.replaceAll('moo', nonCallableNonStringReplace));
}

{
  const positions = [];
  assertEquals('bcb', 'aca'.replaceAll('a',
                                       (searchString, position, string) => {
    assertEquals('a', searchString);
    assertEquals('aca', string);
    positions.push(position);
    return 'b';
  }));
  assertEquals(positions, [0,2]);
}

(function NonGlobalRegex() {
  assertThrows(
      () => { 'ab'.replaceAll(/./, '.'); },
      TypeError);
  assertThrows(
      () => { 'ab'.replaceAll(/./y, '.'); },
      TypeError);
})();

// Tests for stickiness gotcha.
assertEquals('o ppercase!', 'No Uppercase!'.replaceAll(/[A-Z]/g,  ''));
assertEquals('o Uppercase?', 'No Uppercase?'.replaceAll(/[A-Z]/gy, ''));
assertEquals(' UPPERCASE!', 'NO UPPERCASE!'.replaceAll(/[A-Z]/gy, ''));

// Tests for slow path.
assertEquals('a', 'a'.replaceAll(%ConstructConsString('abcdefghijklmn',
                                                      'def'), 'b'));
assertEquals('b', 'abcdefghijklmndef'.replaceAll(
    %ConstructConsString('abcdefghijklmn', 'def'), 'b'));

assertEquals('aaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaa'.replaceAll(
    %ConstructConsString('abcdefghijklmn', 'def'), 'b'));