kol.js 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -9
- package/src/Client.test.ts +245 -64
- package/src/Client.ts +204 -193
- package/src/LoathingDate.test.ts +202 -0
- package/src/LoathingDate.ts +390 -0
- package/src/Player.test.ts +83 -82
- package/src/Player.ts +112 -181
- package/src/domains/AutomatedFuture.test.ts +19 -0
- package/src/domains/AutomatedFuture.ts +46 -0
- package/src/domains/Bookmobile.test.ts +20 -0
- package/src/domains/Bookmobile.ts +66 -0
- package/src/domains/ClanDungeon.ts +230 -0
- package/src/domains/Dreadsylvania.test.ts +424 -0
- package/src/domains/Dreadsylvania.ts +550 -0
- package/src/domains/Familiar.ts +82 -0
- package/src/domains/FloralMercantileExchange.test.ts +20 -0
- package/src/domains/FloralMercantileExchange.ts +51 -0
- package/src/{utils/leaderboard.test.ts → domains/Leaderboard.test.ts} +24 -4
- package/src/domains/Leaderboard.ts +173 -0
- package/src/domains/Players.test.ts +141 -0
- package/src/domains/Players.ts +108 -0
- package/src/domains/Raffle.test.ts +65 -0
- package/src/domains/Raffle.ts +60 -0
- package/src/domains/SkeletonOfCrimboPast.test.ts +55 -0
- package/src/domains/SkeletonOfCrimboPast.ts +38 -0
- package/src/domains/WardrobeOMatic.test.ts +141 -0
- package/src/domains/WardrobeOMatic.ts +650 -0
- package/src/domains/__fixtures__/automated_future.html +1 -0
- package/src/domains/__fixtures__/bookmobile_spooky.html +6 -0
- package/src/domains/__fixtures__/dread/cdr1-current.html +25 -0
- package/src/domains/__fixtures__/dread/cdr1-oldlogs-page0.html +25 -0
- package/src/domains/__fixtures__/dread/cdr2-current.html +25 -0
- package/src/domains/__fixtures__/dread/cdr2-oldlogs-page0.html +25 -0
- package/src/domains/__fixtures__/dread/raid-213013.html +24 -0
- package/src/domains/__fixtures__/dread/raid-217988.html +24 -0
- package/src/domains/__fixtures__/dread/raid-218029.html +24 -0
- package/src/domains/__fixtures__/dread/raid-218205.html +24 -0
- package/src/domains/__fixtures__/dread/raid-218286.html +24 -0
- package/src/domains/__fixtures__/dread/raid-218518.html +24 -0
- package/src/domains/__fixtures__/dread/raid-218519.html +24 -0
- package/src/domains/__fixtures__/flowers.html +229 -0
- package/src/domains/__fixtures__/raidlog.html +1 -0
- package/src/domains/__fixtures__/socp.html +1 -0
- package/src/index.ts +11 -5
- package/src/stats.ts +31 -0
- package/src/utils/kmail.test.ts +110 -0
- package/src/utils/kmail.ts +98 -3
- package/src/utils/utils.ts +45 -2
- package/src/Cache.ts +0 -33
- package/src/utils/leaderboard.ts +0 -78
- /package/src/{utils → domains}/__fixtures__/leaderboard_wotsf.html +0 -0
- /package/src/{__fixtures__ → domains/__fixtures__}/raffle.html +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kol.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./src/index.ts",
|
|
7
|
+
"./domains/*": "./src/domains/*.ts"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"files": [
|
|
7
11
|
"/src"
|
|
@@ -14,9 +18,9 @@
|
|
|
14
18
|
"format": "prettier --write ."
|
|
15
19
|
},
|
|
16
20
|
"devDependencies": {
|
|
17
|
-
"prettier": "^3.
|
|
21
|
+
"prettier": "^3.8.1",
|
|
18
22
|
"typescript": "^5.9.3",
|
|
19
|
-
"vitest": "^4.0.
|
|
23
|
+
"vitest": "^4.0.18"
|
|
20
24
|
},
|
|
21
25
|
"dependencies": {
|
|
22
26
|
"async-mutex": "^0.5.0",
|
|
@@ -24,13 +28,14 @@
|
|
|
24
28
|
"date-fns": "^4.1.0",
|
|
25
29
|
"domhandler": "^5.0.3",
|
|
26
30
|
"domutils": "^3.2.2",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
31
|
+
"emittery": "^1.1.0",
|
|
32
|
+
"entities": "^7.0.1",
|
|
33
|
+
"fetch-cookie": "^3.2.0",
|
|
34
|
+
"htmlparser2": "^10.1.0",
|
|
30
35
|
"image-size": "^2.0.2",
|
|
31
|
-
"
|
|
36
|
+
"kol-rng": "3",
|
|
37
|
+
"ofetch": "^1.5.1",
|
|
32
38
|
"tough-cookie": "^6.0.0",
|
|
33
|
-
"ts-dedent": "^2.2.0"
|
|
34
|
-
"typed-emitter": "^2.1.0"
|
|
39
|
+
"ts-dedent": "^2.2.0"
|
|
35
40
|
}
|
|
36
41
|
}
|
package/src/Client.test.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { describe, expect, it, test, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import { Client } from "./Client.js";
|
|
3
|
+
import { AuthError, Client, RolloverError } from "./Client.js";
|
|
4
4
|
import { loadFixture } from "./testUtils.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
vi.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
client.Client.prototype.fetchJson = json;
|
|
14
|
-
return client;
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const client = new Client("", "");
|
|
6
|
+
function mockSession(fn: () => unknown) {
|
|
7
|
+
return Object.assign(fn, {
|
|
8
|
+
raw: vi.fn(),
|
|
9
|
+
native: fetch,
|
|
10
|
+
create: vi.fn(),
|
|
11
|
+
}) as unknown as Client["session"];
|
|
12
|
+
}
|
|
18
13
|
|
|
19
14
|
describe("LoathingChat", () => {
|
|
15
|
+
const client = new Client("", "");
|
|
16
|
+
|
|
20
17
|
test("Can parse a regular message", async () => {
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
vi.spyOn(client, "fetchJson").mockResolvedValue({});
|
|
19
|
+
vi.spyOn(client, "fetchJson").mockResolvedValueOnce({
|
|
23
20
|
msgs: [
|
|
24
21
|
{
|
|
25
22
|
msg: "testing",
|
|
@@ -51,7 +48,7 @@ describe("LoathingChat", () => {
|
|
|
51
48
|
});
|
|
52
49
|
|
|
53
50
|
test("Can parse a system message for rollover in 5 minutes", async () => {
|
|
54
|
-
|
|
51
|
+
vi.spyOn(client, "fetchJson").mockResolvedValueOnce({
|
|
55
52
|
msgs: [
|
|
56
53
|
{
|
|
57
54
|
msg: "The system will go down for nightly maintenance in 5 minutes.",
|
|
@@ -82,7 +79,7 @@ describe("LoathingChat", () => {
|
|
|
82
79
|
});
|
|
83
80
|
|
|
84
81
|
test("Can parse a system message for rollover in one minute", async () => {
|
|
85
|
-
|
|
82
|
+
vi.spyOn(client, "fetchJson").mockResolvedValueOnce({
|
|
86
83
|
msgs: [
|
|
87
84
|
{
|
|
88
85
|
msg: "The system will go down for nightly maintenance in 1 minute.",
|
|
@@ -113,7 +110,7 @@ describe("LoathingChat", () => {
|
|
|
113
110
|
});
|
|
114
111
|
|
|
115
112
|
test("Can parse a system message for rollover complete", async () => {
|
|
116
|
-
|
|
113
|
+
vi.spyOn(client, "fetchJson").mockResolvedValueOnce({
|
|
117
114
|
msgs: [
|
|
118
115
|
{
|
|
119
116
|
msg: "Rollover is over.",
|
|
@@ -144,7 +141,7 @@ describe("LoathingChat", () => {
|
|
|
144
141
|
});
|
|
145
142
|
|
|
146
143
|
test("Can parse updates", async () => {
|
|
147
|
-
|
|
144
|
+
vi.spyOn(client, "fetchJson").mockResolvedValueOnce({
|
|
148
145
|
output:
|
|
149
146
|
'<hr><center><font color=green><b>Recent Announcements:</b></font></center><p><b>August 21</b> - That pocast you like is back in style. Get it on iTunes, or from the <A style="text-decoration: underline" target=_blank href=http://radio.kingdomofloathing.com/podcast.php>direct RSS feed</a> or <a style="text-decoration: underline" target=_blank href=http://shows.kingdomofloathing.com/The_KoL_Show_20250819.mp3>download the mp3 directly</a>.<p><b>August 15</b> - On September 1st commendations will be given out for softcore Under the Sea runs. The we\'ll be making some nerfs and the leaderboards will be reset for phase 2. <p><b>August 14</b> - Swim into the fall challenge path, 11,037 Leagues Under the Sea!<p><b>March 07</b> - Check out Jick\'s entry in the 2025 7-Day Roguelike Challenge. It\'s called Catacombo and you can <a href=https://zapjackson.itch.io/catacombo target=_blank style="text-decoration: underline">play it here on itch dot eye oh</a>.<p><b>March 06</b> - KoL\'s own Jick wrote a book, a choose-your-own-adventure about escaping from a wizard. You can <a style="text-decoration: underline" target=_blank href=https://www.amazon.com/Escape-Prison-Tower-Wizard-Adventures/dp/B0CTW34JV1/>buy it on Amazon dot com</a>. <p><hr>',
|
|
150
147
|
msgs: [],
|
|
@@ -158,8 +155,10 @@ describe("LoathingChat", () => {
|
|
|
158
155
|
});
|
|
159
156
|
|
|
160
157
|
describe("Skill descriptions", () => {
|
|
158
|
+
const client = new Client("", "");
|
|
159
|
+
|
|
161
160
|
test("can describe a Skill with no bluetext", async () => {
|
|
162
|
-
|
|
161
|
+
vi.spyOn(client, "fetchText").mockResolvedValueOnce(
|
|
163
162
|
await loadFixture(
|
|
164
163
|
__dirname,
|
|
165
164
|
"desc_skill_overload_discarded_refridgerator.html",
|
|
@@ -174,7 +173,7 @@ describe("Skill descriptions", () => {
|
|
|
174
173
|
});
|
|
175
174
|
|
|
176
175
|
test("can describe a Skill with bluetext", async () => {
|
|
177
|
-
|
|
176
|
+
vi.spyOn(client, "fetchText").mockResolvedValueOnce(
|
|
178
177
|
await loadFixture(__dirname, "desc_skill_impetuous_sauciness.html"),
|
|
179
178
|
);
|
|
180
179
|
|
|
@@ -187,21 +186,21 @@ describe("Skill descriptions", () => {
|
|
|
187
186
|
});
|
|
188
187
|
|
|
189
188
|
describe("Familiars", () => {
|
|
189
|
+
const client = new Client("", "");
|
|
190
|
+
|
|
190
191
|
test("can fetch all familiars", async () => {
|
|
191
|
-
|
|
192
|
+
vi.spyOn(client, "fetchText").mockResolvedValueOnce(
|
|
192
193
|
await loadFixture(__dirname, "familiar_in_standard_run.html"),
|
|
193
194
|
);
|
|
194
195
|
|
|
195
196
|
const familiars = await client.getFamiliars();
|
|
196
197
|
|
|
197
|
-
// Current
|
|
198
198
|
expect(familiars).toContainEqual({
|
|
199
199
|
id: 294,
|
|
200
200
|
name: "Jill-of-All-Trades",
|
|
201
201
|
image: "itemimages/darkjill2f.gif",
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
-
// Problematic
|
|
205
204
|
expect(familiars).toContainEqual({
|
|
206
205
|
id: 278,
|
|
207
206
|
name: "Left-Hand Man",
|
|
@@ -213,50 +212,232 @@ describe("Familiars", () => {
|
|
|
213
212
|
image: "otherimages/camelfam_left.gif",
|
|
214
213
|
});
|
|
215
214
|
|
|
216
|
-
// Just to be sure
|
|
217
215
|
expect(familiars).toHaveLength(206);
|
|
218
216
|
});
|
|
219
217
|
});
|
|
220
218
|
|
|
221
|
-
describe("
|
|
222
|
-
it("
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
219
|
+
describe("fetchJson error handling", () => {
|
|
220
|
+
it("throws on non-login errors", async () => {
|
|
221
|
+
const client = new Client("", "");
|
|
222
|
+
vi.spyOn(client, "login").mockResolvedValue(true);
|
|
223
|
+
client.session = mockSession(() => {
|
|
224
|
+
throw new Error("500 Internal Server Error");
|
|
225
|
+
});
|
|
226
|
+
await expect(client.fetchJson("api.php")).rejects.toThrow(
|
|
227
|
+
"500 Internal Server Error",
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("does not retry more than once on non-login errors", async () => {
|
|
232
|
+
const client = new Client("", "");
|
|
233
|
+
vi.spyOn(client, "login").mockResolvedValue(true);
|
|
234
|
+
let calls = 0;
|
|
235
|
+
client.session = mockSession(() => {
|
|
236
|
+
calls++;
|
|
237
|
+
throw new Error("persistent error");
|
|
238
|
+
});
|
|
239
|
+
await expect(client.fetchJson("test.php")).rejects.toThrow(
|
|
240
|
+
"persistent error",
|
|
241
|
+
);
|
|
242
|
+
expect(calls).toBe(1);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("propagates RolloverError", async () => {
|
|
246
|
+
const client = new Client("", "");
|
|
247
|
+
vi.spyOn(client, "login").mockResolvedValue(true);
|
|
248
|
+
client.session = mockSession(() => {
|
|
249
|
+
throw new RolloverError();
|
|
250
|
+
});
|
|
251
|
+
await expect(client.fetchJson("test.php")).rejects.toBeInstanceOf(
|
|
252
|
+
RolloverError,
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("throws AuthError when login fails", async () => {
|
|
257
|
+
const client = new Client("", "");
|
|
258
|
+
vi.spyOn(client, "login").mockResolvedValue(false);
|
|
259
|
+
await expect(client.fetchJson("test.php")).rejects.toBeInstanceOf(
|
|
260
|
+
AuthError,
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe("fetchText error handling", () => {
|
|
266
|
+
it("throws on non-login errors instead of retrying", async () => {
|
|
267
|
+
const client = new Client("", "");
|
|
268
|
+
vi.spyOn(client, "login").mockResolvedValue(true);
|
|
269
|
+
client.session = mockSession(() => {
|
|
270
|
+
throw new Error("Network timeout");
|
|
271
|
+
});
|
|
272
|
+
await expect(client.fetchText("familiar.php")).rejects.toThrow(
|
|
273
|
+
"Network timeout",
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("does not retry more than once on non-login errors", async () => {
|
|
278
|
+
const client = new Client("", "");
|
|
279
|
+
vi.spyOn(client, "login").mockResolvedValue(true);
|
|
280
|
+
let calls = 0;
|
|
281
|
+
client.session = mockSession(() => {
|
|
282
|
+
calls++;
|
|
283
|
+
throw new Error("persistent error");
|
|
284
|
+
});
|
|
285
|
+
await expect(client.fetchText("test.php")).rejects.toThrow(
|
|
286
|
+
"persistent error",
|
|
287
|
+
);
|
|
288
|
+
expect(calls).toBe(1);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("rollover handling", () => {
|
|
293
|
+
it("detects rollover from login.php response", async () => {
|
|
294
|
+
const client = new Client("", "");
|
|
295
|
+
client.session = mockSession(
|
|
296
|
+
() => "The system is currently down for nightly maintenance.",
|
|
297
|
+
);
|
|
298
|
+
await client.login();
|
|
299
|
+
expect(client.isRollover()).toBe(true);
|
|
300
|
+
|
|
301
|
+
// All fetch methods should fail fast
|
|
302
|
+
await expect(client.fetchText("test.php")).rejects.toBeInstanceOf(
|
|
303
|
+
RolloverError,
|
|
304
|
+
);
|
|
305
|
+
await expect(client.fetchJson("test.php")).rejects.toBeInstanceOf(
|
|
306
|
+
RolloverError,
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("fetchText throws RolloverError immediately when in rollover", async () => {
|
|
311
|
+
const client = new Client("", "");
|
|
312
|
+
client.session = mockSession(
|
|
313
|
+
() => "The system is currently down for nightly maintenance",
|
|
314
|
+
);
|
|
315
|
+
await client.login();
|
|
316
|
+
|
|
317
|
+
let sessionCalled = false;
|
|
318
|
+
client.session = mockSession(() => {
|
|
319
|
+
sessionCalled = true;
|
|
320
|
+
return "";
|
|
260
321
|
});
|
|
322
|
+
await expect(client.fetchText("test.php")).rejects.toBeInstanceOf(
|
|
323
|
+
RolloverError,
|
|
324
|
+
);
|
|
325
|
+
expect(sessionCalled).toBe(false);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("fetchJson throws RolloverError immediately when in rollover", async () => {
|
|
329
|
+
const client = new Client("", "");
|
|
330
|
+
client.session = mockSession(
|
|
331
|
+
() => "The system is currently down for nightly maintenance",
|
|
332
|
+
);
|
|
333
|
+
await client.login();
|
|
334
|
+
|
|
335
|
+
let sessionCalled = false;
|
|
336
|
+
client.session = mockSession(() => {
|
|
337
|
+
sessionCalled = true;
|
|
338
|
+
return {};
|
|
339
|
+
});
|
|
340
|
+
await expect(client.fetchJson("test.php")).rejects.toBeInstanceOf(
|
|
341
|
+
RolloverError,
|
|
342
|
+
);
|
|
343
|
+
expect(sessionCalled).toBe(false);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("recovers from rollover when server comes back", async () => {
|
|
347
|
+
vi.useFakeTimers();
|
|
348
|
+
const client = new Client("", "");
|
|
349
|
+
|
|
350
|
+
client.session = mockSession(
|
|
351
|
+
() => "The system is currently down for nightly maintenance",
|
|
352
|
+
);
|
|
353
|
+
await client.login();
|
|
354
|
+
expect(client.isRollover()).toBe(true);
|
|
355
|
+
|
|
356
|
+
client.session = mockSession(() => "<html>normal login page</html>");
|
|
357
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
358
|
+
|
|
359
|
+
expect(client.isRollover()).toBe(false);
|
|
360
|
+
vi.useRealTimers();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("emits rollover event after recovery when login succeeds", async () => {
|
|
364
|
+
vi.useFakeTimers();
|
|
365
|
+
const client = new Client("", "");
|
|
366
|
+
|
|
367
|
+
// Enter rollover
|
|
368
|
+
client.session = mockSession(
|
|
369
|
+
() => "The system is currently down for nightly maintenance",
|
|
370
|
+
);
|
|
371
|
+
await client.login();
|
|
372
|
+
|
|
373
|
+
// Recover: checkForRollover sees normal page, then login flow works
|
|
374
|
+
let callCount = 0;
|
|
375
|
+
client.session = mockSession(() => {
|
|
376
|
+
callCount++;
|
|
377
|
+
// Call 1: #checkForRollover fetches login.php (normal)
|
|
378
|
+
if (callCount === 1) return "<html>normal login page</html>";
|
|
379
|
+
// Call 2+: checkLoggedIn/login succeed
|
|
380
|
+
return { pwd: "abc123" };
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
384
|
+
expect(client.isRollover()).toBe(false);
|
|
385
|
+
|
|
386
|
+
// login needs checkLoggedIn to FAIL first so it goes through the
|
|
387
|
+
// full login flow where the latch is checked. Make first checkLoggedIn
|
|
388
|
+
// fail (no session yet), then login POST + second checkLoggedIn succeed.
|
|
389
|
+
callCount = 0;
|
|
390
|
+
client.session = mockSession(() => {
|
|
391
|
+
callCount++;
|
|
392
|
+
// Call 1: checkLoggedIn → returns non-object (fails validation)
|
|
393
|
+
if (callCount === 1) return "<html>not logged in</html>";
|
|
394
|
+
// Call 2: login POST → success (no rollover pattern)
|
|
395
|
+
if (callCount === 2) return "<html>game frameset</html>";
|
|
396
|
+
// Call 3: second checkLoggedIn → success
|
|
397
|
+
return { pwd: "def456" };
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const rolloverSpy = vi.fn();
|
|
401
|
+
client.on("rollover", rolloverSpy);
|
|
402
|
+
const loggedIn = await client.login();
|
|
403
|
+
expect(loggedIn).toBe(true);
|
|
404
|
+
expect(rolloverSpy).toHaveBeenCalledOnce();
|
|
405
|
+
|
|
406
|
+
vi.useRealTimers();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("stays in rollover if server is unreachable", async () => {
|
|
410
|
+
vi.useFakeTimers();
|
|
411
|
+
const client = new Client("", "");
|
|
412
|
+
|
|
413
|
+
client.session = mockSession(
|
|
414
|
+
() => "The system is currently down for nightly maintenance",
|
|
415
|
+
);
|
|
416
|
+
await client.login();
|
|
417
|
+
expect(client.isRollover()).toBe(true);
|
|
418
|
+
|
|
419
|
+
client.session = mockSession(() => {
|
|
420
|
+
throw new Error("Network error");
|
|
421
|
+
});
|
|
422
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
423
|
+
expect(client.isRollover()).toBe(true);
|
|
424
|
+
|
|
425
|
+
client.session = mockSession(() => "<html>normal login page</html>");
|
|
426
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
427
|
+
expect(client.isRollover()).toBe(false);
|
|
428
|
+
|
|
429
|
+
vi.useRealTimers();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it("does not recurse through fetchText/login during rollover check", async () => {
|
|
433
|
+
const client = new Client("", "");
|
|
434
|
+
let sessionCallCount = 0;
|
|
435
|
+
client.session = mockSession(() => {
|
|
436
|
+
sessionCallCount++;
|
|
437
|
+
return "The system is currently down for nightly maintenance";
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
await client.login();
|
|
441
|
+
expect(sessionCallCount).toBeLessThanOrEqual(3);
|
|
261
442
|
});
|
|
262
443
|
});
|