1package goja
2
3import "testing"
4
5func TestSubstr(t *testing.T) {
6	const SCRIPT = `
7assert.sameValue('abc'.substr(0, false), '', 'start: 0, length: false');
8assert.sameValue('abc'.substr(1, false), '', 'start: 1, length: false');
9assert.sameValue('abc'.substr(2, false), '', 'start: 2, length: false');
10assert.sameValue('abc'.substr(3, false), '', 'start: 3, length: false');
11
12assert.sameValue('abc'.substr(0, NaN), '', 'start: 0, length: NaN');
13assert.sameValue('abc'.substr(1, NaN), '', 'start: 1, length: NaN');
14assert.sameValue('abc'.substr(2, NaN), '', 'start: 2, length: NaN');
15assert.sameValue('abc'.substr(3, NaN), '', 'start: 3, length: NaN');
16
17assert.sameValue('abc'.substr(0, ''), '', 'start: 0, length: ""');
18assert.sameValue('abc'.substr(1, ''), '', 'start: 1, length: ""');
19assert.sameValue('abc'.substr(2, ''), '', 'start: 2, length: ""');
20assert.sameValue('abc'.substr(3, ''), '', 'start: 3, length: ""');
21
22assert.sameValue('abc'.substr(0, null), '', 'start: 0, length: null');
23assert.sameValue('abc'.substr(1, null), '', 'start: 1, length: null');
24assert.sameValue('abc'.substr(2, null), '', 'start: 2, length: null');
25assert.sameValue('abc'.substr(3, null), '', 'start: 3, length: null');
26
27assert.sameValue('abc'.substr(0, -1), '', '0, -1');
28assert.sameValue('abc'.substr(0, -2), '', '0, -2');
29assert.sameValue('abc'.substr(0, -3), '', '0, -3');
30assert.sameValue('abc'.substr(0, -4), '', '0, -4');
31
32assert.sameValue('abc'.substr(1, -1), '', '1, -1');
33assert.sameValue('abc'.substr(1, -2), '', '1, -2');
34assert.sameValue('abc'.substr(1, -3), '', '1, -3');
35assert.sameValue('abc'.substr(1, -4), '', '1, -4');
36
37assert.sameValue('abc'.substr(2, -1), '', '2, -1');
38assert.sameValue('abc'.substr(2, -2), '', '2, -2');
39assert.sameValue('abc'.substr(2, -3), '', '2, -3');
40assert.sameValue('abc'.substr(2, -4), '', '2, -4');
41
42assert.sameValue('abc'.substr(3, -1), '', '3, -1');
43assert.sameValue('abc'.substr(3, -2), '', '3, -2');
44assert.sameValue('abc'.substr(3, -3), '', '3, -3');
45assert.sameValue('abc'.substr(3, -4), '', '3, -4');
46
47assert.sameValue('abc'.substr(0, 1), 'a', '0, 1');
48assert.sameValue('abc'.substr(0, 2), 'ab', '0, 1');
49assert.sameValue('abc'.substr(0, 3), 'abc', '0, 1');
50assert.sameValue('abc'.substr(0, 4), 'abc', '0, 1');
51
52assert.sameValue('abc'.substr(1, 1), 'b', '1, 1');
53assert.sameValue('abc'.substr(1, 2), 'bc', '1, 1');
54assert.sameValue('abc'.substr(1, 3), 'bc', '1, 1');
55assert.sameValue('abc'.substr(1, 4), 'bc', '1, 1');
56
57assert.sameValue('abc'.substr(2, 1), 'c', '2, 1');
58assert.sameValue('abc'.substr(2, 2), 'c', '2, 1');
59assert.sameValue('abc'.substr(2, 3), 'c', '2, 1');
60assert.sameValue('abc'.substr(2, 4), 'c', '2, 1');
61
62assert.sameValue('abc'.substr(3, 1), '', '3, 1');
63assert.sameValue('abc'.substr(3, 2), '', '3, 1');
64assert.sameValue('abc'.substr(3, 3), '', '3, 1');
65assert.sameValue('abc'.substr(3, 4), '', '3, 1');
66
67assert.sameValue('abc'.substr(0), 'abc', 'start: 0, length: unspecified');
68assert.sameValue('abc'.substr(1), 'bc', 'start: 1, length: unspecified');
69assert.sameValue('abc'.substr(2), 'c', 'start: 2, length: unspecified');
70assert.sameValue('abc'.substr(3), '', 'start: 3, length: unspecified');
71
72assert.sameValue(
73  'abc'.substr(0, undefined), 'abc', 'start: 0, length: undefined'
74);
75assert.sameValue(
76  'abc'.substr(1, undefined), 'bc', 'start: 1, length: undefined'
77);
78assert.sameValue(
79  'abc'.substr(2, undefined), 'c', 'start: 2, length: undefined'
80);
81assert.sameValue(
82  'abc'.substr(3, undefined), '', 'start: 3, length: undefined'
83);
84
85assert.sameValue('A—', String.fromCharCode(65, 0x2014));
86
87	`
88
89	testScript1(TESTLIB+SCRIPT, _undefined, t)
90}
91
92func TestStringMatchSym(t *testing.T) {
93	const SCRIPT = `
94function Prefix(p) {
95	this.p = p;
96}
97
98Prefix.prototype[Symbol.match] = function(s) {
99	return s.substring(0, this.p.length) === this.p;
100}
101
102var prefix1 = new Prefix("abc");
103var prefix2 = new Prefix("def");
104
105"abc123".match(prefix1) === true && "abc123".match(prefix2) === false &&
106"def123".match(prefix1) === false && "def123".match(prefix2) === true;
107`
108	testScript1(SCRIPT, valueTrue, t)
109}
110
111func TestStringMatchAllSym(t *testing.T) {
112	const SCRIPT = `
113function Prefix(p) {
114	this.p = p;
115}
116
117Prefix.prototype[Symbol.matchAll] = function(s) {
118	return s.substring(0, this.p.length) === this.p;
119}
120
121var prefix1 = new Prefix("abc");
122var prefix2 = new Prefix("def");
123
124"abc123".matchAll(prefix1) === true && "abc123".matchAll(prefix2) === false &&
125"def123".matchAll(prefix1) === false && "def123".matchAll(prefix2) === true;
126`
127	testScript1(SCRIPT, valueTrue, t)
128}
129
130func TestGenericSplitter(t *testing.T) {
131	const SCRIPT = `
132function MyRegexp(pattern, flags) {
133	if (pattern instanceof MyRegexp) {
134		pattern = pattern.wrapped;
135	}
136	this.wrapped = new RegExp(pattern, flags);
137}
138
139MyRegexp.prototype.exec = function() {
140	return this.wrapped.exec.apply(this.wrapped, arguments);
141}
142
143Object.defineProperty(MyRegexp.prototype, "lastIndex", {
144	get: function() {
145		return this.wrapped.lastIndex;
146	},
147	set: function(v) {
148		this.wrapped.lastIndex = v;
149	}
150});
151
152Object.defineProperty(MyRegexp.prototype, "flags", {
153	get: function() {
154		return this.wrapped.flags;
155	}
156});
157
158MyRegexp[Symbol.species] = MyRegexp;
159MyRegexp.prototype[Symbol.split] = RegExp.prototype[Symbol.split];
160
161var r = new MyRegexp(/ /);
162var res = "a b c".split(r);
163res.length === 3 && res[0] === "a" && res[1] === "b" && res[2] === "c";
164`
165	testScript1(SCRIPT, valueTrue, t)
166}
167
168func TestStringIterSurrPair(t *testing.T) {
169	const SCRIPT = `
170var lo = '\uD834';
171var hi = '\uDF06';
172var pair = lo + hi;
173var string = 'a' + pair + 'b' + lo + pair + hi + lo;
174var iterator = string[Symbol.iterator]();
175var result;
176
177result = iterator.next();
178if (result.value !== 'a') {
179	throw new Error("at 0: " + result.value);
180}
181result = iterator.next();
182if (result.value !== pair) {
183	throw new Error("at 1: " + result.value);
184}
185
186`
187	testScript1(SCRIPT, _undefined, t)
188}
189
190func TestValueStringBuilder(t *testing.T) {
191	t.Run("substringASCII", func(t *testing.T) {
192		t.Parallel()
193		var sb valueStringBuilder
194		str := newStringValue("a\U00010000b")
195		sb.WriteSubstring(str, 0, 1)
196		res := sb.String()
197		if res != asciiString("a") {
198			t.Fatal(res)
199		}
200	})
201
202	t.Run("substringASCIIPure", func(t *testing.T) {
203		t.Parallel()
204		var sb valueStringBuilder
205		str := newStringValue("ab")
206		sb.WriteSubstring(str, 0, 1)
207		res := sb.String()
208		if res != asciiString("a") {
209			t.Fatal(res)
210		}
211	})
212
213	t.Run("substringUnicode", func(t *testing.T) {
214		t.Parallel()
215		var sb valueStringBuilder
216		str := newStringValue("a\U00010000b")
217		sb.WriteSubstring(str, 1, 3)
218		res := sb.String()
219		if !res.SameAs(unicodeStringFromRunes([]rune{0x10000})) {
220			t.Fatal(res)
221		}
222	})
223
224	t.Run("substringASCIIUnicode", func(t *testing.T) {
225		t.Parallel()
226		var sb valueStringBuilder
227		str := newStringValue("a\U00010000b")
228		sb.WriteSubstring(str, 0, 2)
229		res := sb.String()
230		if !res.SameAs(unicodeStringFromRunes([]rune{'a', 0xD800})) {
231			t.Fatal(res)
232		}
233	})
234
235	t.Run("substringUnicodeASCII", func(t *testing.T) {
236		t.Parallel()
237		var sb valueStringBuilder
238		str := newStringValue("a\U00010000b")
239		sb.WriteSubstring(str, 2, 4)
240		res := sb.String()
241		if !res.SameAs(unicodeStringFromRunes([]rune{0xDC00, 'b'})) {
242			t.Fatal(res)
243		}
244	})
245
246	t.Run("concatSubstringUnicodeASCII", func(t *testing.T) {
247		t.Parallel()
248		var sb valueStringBuilder
249		sb.WriteString(newStringValue("юникод"))
250		sb.WriteSubstring(asciiString(" ascii"), 0, 6)
251		if res := sb.String(); !res.SameAs(newStringValue("юникод ascii")) {
252			t.Fatal(res)
253		}
254	})
255}
256