quake2ts 0.0.562 → 0.0.564
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 +1 -1
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/types/assets/crossReference.d.ts +9 -27
- package/packages/engine/dist/types/assets/crossReference.d.ts.map +1 -1
- package/packages/engine/dist/types/assets/streamingPak.d.ts +28 -0
- package/packages/engine/dist/types/assets/streamingPak.d.ts.map +1 -0
- package/packages/test-utils/dist/index.cjs +435 -5
- package/packages/test-utils/dist/index.cjs.map +1 -1
- package/packages/test-utils/dist/index.d.cts +216 -10
- package/packages/test-utils/dist/index.d.ts +216 -10
- package/packages/test-utils/dist/index.js +414 -5
- package/packages/test-utils/dist/index.js.map +1 -1
|
@@ -30,24 +30,31 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
HandshakeStage: () => HandshakeStage,
|
|
33
34
|
InputInjector: () => InputInjector,
|
|
34
35
|
MockPointerLock: () => MockPointerLock,
|
|
35
36
|
MockTransport: () => MockTransport,
|
|
36
37
|
captureAudioEvents: () => captureAudioEvents,
|
|
37
38
|
captureCanvasDrawCalls: () => captureCanvasDrawCalls,
|
|
39
|
+
captureGameScreenshot: () => captureGameScreenshot,
|
|
38
40
|
captureGameState: () => captureGameState,
|
|
41
|
+
compareScreenshots: () => compareScreenshots,
|
|
39
42
|
createBinaryStreamMock: () => createBinaryStreamMock,
|
|
40
43
|
createBinaryWriterMock: () => createBinaryWriterMock,
|
|
41
44
|
createControlledTimer: () => createControlledTimer,
|
|
45
|
+
createCustomNetworkCondition: () => createCustomNetworkCondition,
|
|
46
|
+
createDeltaSnapshot: () => createDeltaSnapshot,
|
|
42
47
|
createEntity: () => createEntity,
|
|
43
48
|
createEntityStateFactory: () => createEntityStateFactory,
|
|
44
49
|
createGameStateSnapshotFactory: () => createGameStateSnapshotFactory,
|
|
45
50
|
createMockAudioContext: () => createMockAudioContext,
|
|
46
51
|
createMockCanvas: () => createMockCanvas,
|
|
47
52
|
createMockCanvasContext2D: () => createMockCanvasContext2D,
|
|
53
|
+
createMockConnection: () => createMockConnection,
|
|
48
54
|
createMockEngine: () => createMockEngine,
|
|
49
55
|
createMockGame: () => createMockGame,
|
|
50
56
|
createMockGameState: () => createMockGameState,
|
|
57
|
+
createMockHandshake: () => createMockHandshake,
|
|
51
58
|
createMockImage: () => createMockImage,
|
|
52
59
|
createMockImageData: () => createMockImageData,
|
|
53
60
|
createMockIndexedDB: () => createMockIndexedDB,
|
|
@@ -62,13 +69,17 @@ __export(index_exports, {
|
|
|
62
69
|
createMockSessionStorage: () => createMockSessionStorage,
|
|
63
70
|
createMockTransport: () => createMockTransport,
|
|
64
71
|
createMockUDPSocket: () => createMockUDPSocket,
|
|
72
|
+
createMockUserInfo: () => createMockUserInfo,
|
|
65
73
|
createMockWebGL2Context: () => createMockWebGL2Context,
|
|
74
|
+
createMultiplayerTestScenario: () => createMultiplayerTestScenario,
|
|
66
75
|
createNetChanMock: () => createNetChanMock,
|
|
67
76
|
createPlayerStateFactory: () => createPlayerStateFactory,
|
|
68
77
|
createPlaywrightTestClient: () => createPlaywrightTestClient,
|
|
78
|
+
createServerSnapshot: () => createServerSnapshot,
|
|
69
79
|
createSpawnContext: () => createSpawnContext,
|
|
70
80
|
createStorageTestScenario: () => createStorageTestScenario,
|
|
71
81
|
createTestContext: () => createTestContext,
|
|
82
|
+
createVisualTestScenario: () => createVisualTestScenario,
|
|
72
83
|
intersects: () => import_shared3.intersects,
|
|
73
84
|
ladderTrace: () => import_shared3.ladderTrace,
|
|
74
85
|
makeAxisBrush: () => makeAxisBrush,
|
|
@@ -78,14 +89,24 @@ __export(index_exports, {
|
|
|
78
89
|
makeLeafModel: () => makeLeafModel,
|
|
79
90
|
makeNode: () => makeNode,
|
|
80
91
|
makePlane: () => makePlane,
|
|
92
|
+
serializeUserInfo: () => serializeUserInfo,
|
|
81
93
|
setupBrowserEnvironment: () => setupBrowserEnvironment,
|
|
82
94
|
setupMockAudioContext: () => setupMockAudioContext,
|
|
83
95
|
setupNodeEnvironment: () => setupNodeEnvironment,
|
|
84
96
|
simulateFrames: () => simulateFrames,
|
|
85
97
|
simulateFramesWithMock: () => simulateFramesWithMock,
|
|
98
|
+
simulateHandshake: () => simulateHandshake,
|
|
99
|
+
simulateNetworkCondition: () => simulateNetworkCondition,
|
|
100
|
+
simulatePlayerInput: () => simulatePlayerInput,
|
|
101
|
+
simulatePlayerJoin: () => simulatePlayerJoin,
|
|
102
|
+
simulatePlayerLeave: () => simulatePlayerLeave,
|
|
103
|
+
simulateServerTick: () => simulateServerTick,
|
|
104
|
+
simulateSnapshotDelivery: () => simulateSnapshotDelivery,
|
|
86
105
|
stairTrace: () => import_shared3.stairTrace,
|
|
87
106
|
teardownBrowserEnvironment: () => teardownBrowserEnvironment,
|
|
88
107
|
teardownMockAudioContext: () => teardownMockAudioContext,
|
|
108
|
+
throttleBandwidth: () => throttleBandwidth,
|
|
109
|
+
verifySnapshotConsistency: () => verifySnapshotConsistency,
|
|
89
110
|
waitForGameReady: () => waitForGameReady
|
|
90
111
|
});
|
|
91
112
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -692,6 +713,214 @@ function createMockServer(overrides) {
|
|
|
692
713
|
};
|
|
693
714
|
}
|
|
694
715
|
|
|
716
|
+
// src/server/mocks/connection.ts
|
|
717
|
+
var import_server2 = require("@quake2ts/server");
|
|
718
|
+
var HandshakeStage = /* @__PURE__ */ ((HandshakeStage2) => {
|
|
719
|
+
HandshakeStage2[HandshakeStage2["None"] = 0] = "None";
|
|
720
|
+
HandshakeStage2[HandshakeStage2["Challenge"] = 1] = "Challenge";
|
|
721
|
+
HandshakeStage2[HandshakeStage2["Connect"] = 2] = "Connect";
|
|
722
|
+
HandshakeStage2[HandshakeStage2["Info"] = 3] = "Info";
|
|
723
|
+
HandshakeStage2[HandshakeStage2["Active"] = 4] = "Active";
|
|
724
|
+
return HandshakeStage2;
|
|
725
|
+
})(HandshakeStage || {});
|
|
726
|
+
function serializeUserInfo(info) {
|
|
727
|
+
return Object.entries(info).reduce((acc, [key, value]) => {
|
|
728
|
+
if (value !== void 0) {
|
|
729
|
+
return `${acc}\\${key}\\${value}`;
|
|
730
|
+
}
|
|
731
|
+
return acc;
|
|
732
|
+
}, "");
|
|
733
|
+
}
|
|
734
|
+
function createMockUserInfo(overrides) {
|
|
735
|
+
return {
|
|
736
|
+
name: "Player",
|
|
737
|
+
skin: "male/grunt",
|
|
738
|
+
model: "male/grunt",
|
|
739
|
+
fov: 90,
|
|
740
|
+
hand: 0,
|
|
741
|
+
rate: 25e3,
|
|
742
|
+
msg: 1,
|
|
743
|
+
...overrides
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function createMockConnection(state = import_server2.ClientState.Connected, overrides) {
|
|
747
|
+
return createMockServerClient(0, {
|
|
748
|
+
state,
|
|
749
|
+
userInfo: serializeUserInfo(createMockUserInfo()),
|
|
750
|
+
challenge: Math.floor(Math.random() * 1e5),
|
|
751
|
+
...overrides
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
function createMockHandshake(stage = 0 /* None */) {
|
|
755
|
+
return {
|
|
756
|
+
stage,
|
|
757
|
+
clientNum: -1,
|
|
758
|
+
challenge: 0,
|
|
759
|
+
qport: Math.floor(Math.random() * 65536)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
async function simulateHandshake(client, server) {
|
|
763
|
+
const challenge = Math.floor(Math.random() * 1e5);
|
|
764
|
+
client.challenge = challenge;
|
|
765
|
+
if (server && server.clients) {
|
|
766
|
+
}
|
|
767
|
+
client.state = import_server2.ClientState.Connected;
|
|
768
|
+
client.state = import_server2.ClientState.Active;
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// src/server/helpers/multiplayer.ts
|
|
773
|
+
var import_server3 = require("@quake2ts/server");
|
|
774
|
+
function createMultiplayerTestScenario(numPlayers = 2) {
|
|
775
|
+
const baseServer = createMockServerState();
|
|
776
|
+
const clients = [];
|
|
777
|
+
const entities = [];
|
|
778
|
+
const server = {
|
|
779
|
+
...baseServer,
|
|
780
|
+
clients: new Array(numPlayers).fill(null),
|
|
781
|
+
entities: []
|
|
782
|
+
};
|
|
783
|
+
for (let i = 0; i < numPlayers; i++) {
|
|
784
|
+
const client = createMockServerClient(i, {
|
|
785
|
+
state: import_server3.ClientState.Active,
|
|
786
|
+
userInfo: serializeUserInfo(createMockUserInfo({ name: `Player${i}` }))
|
|
787
|
+
});
|
|
788
|
+
const entity = {
|
|
789
|
+
classname: "player",
|
|
790
|
+
s: { origin: { x: 0, y: 0, z: 0 }, number: i + 1 },
|
|
791
|
+
client
|
|
792
|
+
};
|
|
793
|
+
client.edict = entity;
|
|
794
|
+
server.clients[i] = client;
|
|
795
|
+
clients.push(client);
|
|
796
|
+
entities.push(entity);
|
|
797
|
+
}
|
|
798
|
+
server.entities = entities;
|
|
799
|
+
return {
|
|
800
|
+
server,
|
|
801
|
+
clients,
|
|
802
|
+
entities
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
async function simulatePlayerJoin(server, userInfo) {
|
|
806
|
+
const index = server.clients.findIndex((c) => !c || c.state === import_server3.ClientState.Free);
|
|
807
|
+
if (index === -1) {
|
|
808
|
+
throw new Error("Server full");
|
|
809
|
+
}
|
|
810
|
+
const client = createMockServerClient(index, {
|
|
811
|
+
state: import_server3.ClientState.Connected,
|
|
812
|
+
userInfo: serializeUserInfo(createMockUserInfo(userInfo))
|
|
813
|
+
});
|
|
814
|
+
server.clients[index] = client;
|
|
815
|
+
client.state = import_server3.ClientState.Active;
|
|
816
|
+
const entity = {
|
|
817
|
+
classname: "player",
|
|
818
|
+
s: { origin: { x: 0, y: 0, z: 0 }, number: index + 1 },
|
|
819
|
+
client
|
|
820
|
+
};
|
|
821
|
+
client.edict = entity;
|
|
822
|
+
if (server.entities && Array.isArray(server.entities)) {
|
|
823
|
+
server.entities[index + 1] = entity;
|
|
824
|
+
}
|
|
825
|
+
return client;
|
|
826
|
+
}
|
|
827
|
+
function simulatePlayerLeave(server, clientNum) {
|
|
828
|
+
const client = server.clients[clientNum];
|
|
829
|
+
if (client) {
|
|
830
|
+
client.state = import_server3.ClientState.Free;
|
|
831
|
+
client.edict = null;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
function simulateServerTick(server, deltaTime = 0.1) {
|
|
835
|
+
server.time += deltaTime;
|
|
836
|
+
server.frame++;
|
|
837
|
+
server.clients.forEach((client) => {
|
|
838
|
+
if (client && client.state === import_server3.ClientState.Active) {
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
function simulatePlayerInput(client, input) {
|
|
843
|
+
const cmd = {
|
|
844
|
+
msec: 100,
|
|
845
|
+
buttons: 0,
|
|
846
|
+
angles: { x: 0, y: 0, z: 0 },
|
|
847
|
+
forwardmove: 0,
|
|
848
|
+
sidemove: 0,
|
|
849
|
+
upmove: 0,
|
|
850
|
+
sequence: client.lastCmd.sequence + 1,
|
|
851
|
+
lightlevel: 0,
|
|
852
|
+
impulse: 0,
|
|
853
|
+
...input
|
|
854
|
+
};
|
|
855
|
+
client.lastCmd = cmd;
|
|
856
|
+
client.commandQueue.push(cmd);
|
|
857
|
+
client.commandCount++;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// src/server/helpers/snapshot.ts
|
|
861
|
+
function createServerSnapshot(serverState, clientNum) {
|
|
862
|
+
const visibleEntities = [];
|
|
863
|
+
if (serverState.baselines) {
|
|
864
|
+
serverState.baselines.forEach((ent, index) => {
|
|
865
|
+
if (ent && index !== clientNum + 1) {
|
|
866
|
+
visibleEntities.push({ ...ent });
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
serverTime: serverState.time,
|
|
872
|
+
playerState: {
|
|
873
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
874
|
+
viewangles: { x: 0, y: 0, z: 0 },
|
|
875
|
+
pm_type: 0
|
|
876
|
+
},
|
|
877
|
+
entities: visibleEntities
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
function createDeltaSnapshot(oldSnapshot, newSnapshot) {
|
|
881
|
+
const deltaEntities = [];
|
|
882
|
+
const removedEntities = [];
|
|
883
|
+
const oldMap = new Map(oldSnapshot.entities.map((e) => [e.number, e]));
|
|
884
|
+
const newMap = new Map(newSnapshot.entities.map((e) => [e.number, e]));
|
|
885
|
+
newSnapshot.entities.forEach((newEnt) => {
|
|
886
|
+
const oldEnt = oldMap.get(newEnt.number);
|
|
887
|
+
if (!oldEnt) {
|
|
888
|
+
deltaEntities.push(newEnt);
|
|
889
|
+
} else if (JSON.stringify(newEnt) !== JSON.stringify(oldEnt)) {
|
|
890
|
+
deltaEntities.push(newEnt);
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
oldSnapshot.entities.forEach((oldEnt) => {
|
|
894
|
+
if (!newMap.has(oldEnt.number)) {
|
|
895
|
+
removedEntities.push(oldEnt.number);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
return {
|
|
899
|
+
snapshot: newSnapshot,
|
|
900
|
+
deltaEntities,
|
|
901
|
+
removedEntities
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function verifySnapshotConsistency(snapshots) {
|
|
905
|
+
const report = { valid: true, errors: [] };
|
|
906
|
+
if (snapshots.length < 2) return report;
|
|
907
|
+
for (let i = 1; i < snapshots.length; i++) {
|
|
908
|
+
const prev = snapshots[i - 1];
|
|
909
|
+
const curr = snapshots[i];
|
|
910
|
+
if (curr.serverTime <= prev.serverTime) {
|
|
911
|
+
report.valid = false;
|
|
912
|
+
report.errors.push(`Snapshot ${i} has time ${curr.serverTime} <= prev ${prev.serverTime}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return report;
|
|
916
|
+
}
|
|
917
|
+
async function simulateSnapshotDelivery(snapshot, reliability = 1) {
|
|
918
|
+
if (Math.random() > reliability) {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
return snapshot;
|
|
922
|
+
}
|
|
923
|
+
|
|
695
924
|
// src/setup/browser.ts
|
|
696
925
|
var import_jsdom = require("jsdom");
|
|
697
926
|
var import_canvas = require("@napi-rs/canvas");
|
|
@@ -1169,8 +1398,8 @@ function createMockImage(width, height, src) {
|
|
|
1169
1398
|
}
|
|
1170
1399
|
|
|
1171
1400
|
// src/setup/node.ts
|
|
1172
|
-
function setupNodeEnvironment() {
|
|
1173
|
-
if (typeof global.fetch === "undefined") {
|
|
1401
|
+
function setupNodeEnvironment(options = {}) {
|
|
1402
|
+
if (options.polyfillFetch && typeof global.fetch === "undefined") {
|
|
1174
1403
|
}
|
|
1175
1404
|
}
|
|
1176
1405
|
|
|
@@ -1199,6 +1428,61 @@ function createMockIndexedDB() {
|
|
|
1199
1428
|
return indexedDB;
|
|
1200
1429
|
}
|
|
1201
1430
|
function createStorageTestScenario(storageType = "local") {
|
|
1431
|
+
if (storageType === "indexed") {
|
|
1432
|
+
const dbName = `test-db-${Math.random().toString(36).substring(7)}`;
|
|
1433
|
+
const storeName = "test-store";
|
|
1434
|
+
const storage2 = createMockIndexedDB();
|
|
1435
|
+
return {
|
|
1436
|
+
storage: storage2,
|
|
1437
|
+
populate: async (data) => {
|
|
1438
|
+
return new Promise((resolve, reject) => {
|
|
1439
|
+
const req = storage2.open(dbName, 1);
|
|
1440
|
+
req.onupgradeneeded = (e) => {
|
|
1441
|
+
const db = e.target.result;
|
|
1442
|
+
db.createObjectStore(storeName);
|
|
1443
|
+
};
|
|
1444
|
+
req.onsuccess = (e) => {
|
|
1445
|
+
const db = e.target.result;
|
|
1446
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
1447
|
+
const store = tx.objectStore(storeName);
|
|
1448
|
+
Object.entries(data).forEach(([k, v]) => store.put(v, k));
|
|
1449
|
+
tx.oncomplete = () => {
|
|
1450
|
+
db.close();
|
|
1451
|
+
resolve();
|
|
1452
|
+
};
|
|
1453
|
+
tx.onerror = () => reject(tx.error);
|
|
1454
|
+
};
|
|
1455
|
+
req.onerror = () => reject(req.error);
|
|
1456
|
+
});
|
|
1457
|
+
},
|
|
1458
|
+
verify: async (key, value) => {
|
|
1459
|
+
return new Promise((resolve, reject) => {
|
|
1460
|
+
const req = storage2.open(dbName, 1);
|
|
1461
|
+
req.onsuccess = (e) => {
|
|
1462
|
+
const db = e.target.result;
|
|
1463
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
1464
|
+
db.close();
|
|
1465
|
+
resolve(false);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
const tx = db.transaction(storeName, "readonly");
|
|
1469
|
+
const store = tx.objectStore(storeName);
|
|
1470
|
+
const getReq = store.get(key);
|
|
1471
|
+
getReq.onsuccess = () => {
|
|
1472
|
+
const result = getReq.result === value;
|
|
1473
|
+
db.close();
|
|
1474
|
+
resolve(result);
|
|
1475
|
+
};
|
|
1476
|
+
getReq.onerror = () => {
|
|
1477
|
+
db.close();
|
|
1478
|
+
resolve(false);
|
|
1479
|
+
};
|
|
1480
|
+
};
|
|
1481
|
+
req.onerror = () => reject(req.error);
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1202
1486
|
const storage = storageType === "local" ? createMockLocalStorage() : createMockSessionStorage();
|
|
1203
1487
|
return {
|
|
1204
1488
|
storage,
|
|
@@ -1213,7 +1497,7 @@ function createStorageTestScenario(storageType = "local") {
|
|
|
1213
1497
|
|
|
1214
1498
|
// src/setup/audio.ts
|
|
1215
1499
|
function createMockAudioContext() {
|
|
1216
|
-
|
|
1500
|
+
const context = {
|
|
1217
1501
|
createGain: () => ({
|
|
1218
1502
|
connect: () => {
|
|
1219
1503
|
},
|
|
@@ -1262,8 +1546,23 @@ function createMockAudioContext() {
|
|
|
1262
1546
|
sampleRate,
|
|
1263
1547
|
numberOfChannels: channels,
|
|
1264
1548
|
getChannelData: () => new Float32Array(length)
|
|
1265
|
-
})
|
|
1549
|
+
}),
|
|
1550
|
+
// Helper to track events if needed
|
|
1551
|
+
_events: []
|
|
1266
1552
|
};
|
|
1553
|
+
return new Proxy(context, {
|
|
1554
|
+
get(target, prop, receiver) {
|
|
1555
|
+
if (prop === "_events") return target._events;
|
|
1556
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1557
|
+
if (typeof value === "function") {
|
|
1558
|
+
return (...args) => {
|
|
1559
|
+
target._events.push({ type: String(prop), args });
|
|
1560
|
+
return Reflect.apply(value, target, args);
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
return value;
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1267
1566
|
}
|
|
1268
1567
|
function setupMockAudioContext() {
|
|
1269
1568
|
if (typeof global.AudioContext === "undefined" && typeof global.window !== "undefined") {
|
|
@@ -1284,7 +1583,7 @@ function teardownMockAudioContext() {
|
|
|
1284
1583
|
}
|
|
1285
1584
|
}
|
|
1286
1585
|
function captureAudioEvents(context) {
|
|
1287
|
-
return [];
|
|
1586
|
+
return context._events || [];
|
|
1288
1587
|
}
|
|
1289
1588
|
|
|
1290
1589
|
// src/setup/timing.ts
|
|
@@ -1576,26 +1875,143 @@ async function captureGameState(page) {
|
|
|
1576
1875
|
return {};
|
|
1577
1876
|
});
|
|
1578
1877
|
}
|
|
1878
|
+
|
|
1879
|
+
// src/e2e/network.ts
|
|
1880
|
+
var CONDITIONS = {
|
|
1881
|
+
"good": {
|
|
1882
|
+
offline: false,
|
|
1883
|
+
downloadThroughput: 10 * 1024 * 1024,
|
|
1884
|
+
// 10 Mbps
|
|
1885
|
+
uploadThroughput: 5 * 1024 * 1024,
|
|
1886
|
+
// 5 Mbps
|
|
1887
|
+
latency: 20
|
|
1888
|
+
},
|
|
1889
|
+
"slow": {
|
|
1890
|
+
offline: false,
|
|
1891
|
+
downloadThroughput: 500 * 1024,
|
|
1892
|
+
// 500 Kbps
|
|
1893
|
+
uploadThroughput: 500 * 1024,
|
|
1894
|
+
latency: 400
|
|
1895
|
+
},
|
|
1896
|
+
"unstable": {
|
|
1897
|
+
offline: false,
|
|
1898
|
+
downloadThroughput: 1 * 1024 * 1024,
|
|
1899
|
+
uploadThroughput: 1 * 1024 * 1024,
|
|
1900
|
+
latency: 100
|
|
1901
|
+
},
|
|
1902
|
+
"offline": {
|
|
1903
|
+
offline: true,
|
|
1904
|
+
downloadThroughput: 0,
|
|
1905
|
+
uploadThroughput: 0,
|
|
1906
|
+
latency: 0
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
function simulateNetworkCondition(condition) {
|
|
1910
|
+
const config = CONDITIONS[condition];
|
|
1911
|
+
return createCustomNetworkCondition(config.latency, 0, 0, config);
|
|
1912
|
+
}
|
|
1913
|
+
function createCustomNetworkCondition(latency, jitter = 0, packetLoss = 0, baseConfig) {
|
|
1914
|
+
return {
|
|
1915
|
+
async apply(page) {
|
|
1916
|
+
const client = await page.context().newCDPSession(page);
|
|
1917
|
+
await client.send("Network.enable");
|
|
1918
|
+
await client.send("Network.emulateNetworkConditions", {
|
|
1919
|
+
offline: baseConfig?.offline || false,
|
|
1920
|
+
latency: latency + Math.random() * jitter,
|
|
1921
|
+
downloadThroughput: baseConfig?.downloadThroughput || -1,
|
|
1922
|
+
uploadThroughput: baseConfig?.uploadThroughput || -1
|
|
1923
|
+
});
|
|
1924
|
+
},
|
|
1925
|
+
async clear(page) {
|
|
1926
|
+
const client = await page.context().newCDPSession(page);
|
|
1927
|
+
await client.send("Network.emulateNetworkConditions", {
|
|
1928
|
+
offline: false,
|
|
1929
|
+
latency: 0,
|
|
1930
|
+
downloadThroughput: -1,
|
|
1931
|
+
uploadThroughput: -1
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
async function throttleBandwidth(page, bytesPerSecond) {
|
|
1937
|
+
const simulator = createCustomNetworkCondition(0, 0, 0, {
|
|
1938
|
+
offline: false,
|
|
1939
|
+
latency: 0,
|
|
1940
|
+
downloadThroughput: bytesPerSecond,
|
|
1941
|
+
uploadThroughput: bytesPerSecond
|
|
1942
|
+
});
|
|
1943
|
+
await simulator.apply(page);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
// src/e2e/visual.ts
|
|
1947
|
+
var import_path = __toESM(require("path"), 1);
|
|
1948
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
1949
|
+
async function captureGameScreenshot(page, name, options = {}) {
|
|
1950
|
+
const dir = options.dir || "__screenshots__";
|
|
1951
|
+
const screenshotPath = import_path.default.join(dir, `${name}.png`);
|
|
1952
|
+
await import_promises.default.mkdir(dir, { recursive: true });
|
|
1953
|
+
return await page.screenshot({
|
|
1954
|
+
path: screenshotPath,
|
|
1955
|
+
fullPage: options.fullPage ?? false,
|
|
1956
|
+
animations: "disabled",
|
|
1957
|
+
caret: "hide"
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
async function compareScreenshots(baseline, current, threshold = 0.1) {
|
|
1961
|
+
if (baseline.equals(current)) {
|
|
1962
|
+
return { pixelDiff: 0, matched: true };
|
|
1963
|
+
}
|
|
1964
|
+
return {
|
|
1965
|
+
pixelDiff: -1,
|
|
1966
|
+
// Unknown magnitude
|
|
1967
|
+
matched: false
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
function createVisualTestScenario(page, sceneName) {
|
|
1971
|
+
return {
|
|
1972
|
+
async capture(snapshotName) {
|
|
1973
|
+
return await captureGameScreenshot(page, `${sceneName}-${snapshotName}`);
|
|
1974
|
+
},
|
|
1975
|
+
async compare(snapshotName, baselineDir) {
|
|
1976
|
+
const name = `${sceneName}-${snapshotName}`;
|
|
1977
|
+
const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
|
|
1978
|
+
try {
|
|
1979
|
+
const baselinePath = import_path.default.join(baselineDir, `${name}.png`);
|
|
1980
|
+
const baseline = await import_promises.default.readFile(baselinePath);
|
|
1981
|
+
return await compareScreenshots(baseline, current);
|
|
1982
|
+
} catch (e) {
|
|
1983
|
+
return { pixelDiff: -1, matched: false };
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1579
1988
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1580
1989
|
0 && (module.exports = {
|
|
1990
|
+
HandshakeStage,
|
|
1581
1991
|
InputInjector,
|
|
1582
1992
|
MockPointerLock,
|
|
1583
1993
|
MockTransport,
|
|
1584
1994
|
captureAudioEvents,
|
|
1585
1995
|
captureCanvasDrawCalls,
|
|
1996
|
+
captureGameScreenshot,
|
|
1586
1997
|
captureGameState,
|
|
1998
|
+
compareScreenshots,
|
|
1587
1999
|
createBinaryStreamMock,
|
|
1588
2000
|
createBinaryWriterMock,
|
|
1589
2001
|
createControlledTimer,
|
|
2002
|
+
createCustomNetworkCondition,
|
|
2003
|
+
createDeltaSnapshot,
|
|
1590
2004
|
createEntity,
|
|
1591
2005
|
createEntityStateFactory,
|
|
1592
2006
|
createGameStateSnapshotFactory,
|
|
1593
2007
|
createMockAudioContext,
|
|
1594
2008
|
createMockCanvas,
|
|
1595
2009
|
createMockCanvasContext2D,
|
|
2010
|
+
createMockConnection,
|
|
1596
2011
|
createMockEngine,
|
|
1597
2012
|
createMockGame,
|
|
1598
2013
|
createMockGameState,
|
|
2014
|
+
createMockHandshake,
|
|
1599
2015
|
createMockImage,
|
|
1600
2016
|
createMockImageData,
|
|
1601
2017
|
createMockIndexedDB,
|
|
@@ -1610,13 +2026,17 @@ async function captureGameState(page) {
|
|
|
1610
2026
|
createMockSessionStorage,
|
|
1611
2027
|
createMockTransport,
|
|
1612
2028
|
createMockUDPSocket,
|
|
2029
|
+
createMockUserInfo,
|
|
1613
2030
|
createMockWebGL2Context,
|
|
2031
|
+
createMultiplayerTestScenario,
|
|
1614
2032
|
createNetChanMock,
|
|
1615
2033
|
createPlayerStateFactory,
|
|
1616
2034
|
createPlaywrightTestClient,
|
|
2035
|
+
createServerSnapshot,
|
|
1617
2036
|
createSpawnContext,
|
|
1618
2037
|
createStorageTestScenario,
|
|
1619
2038
|
createTestContext,
|
|
2039
|
+
createVisualTestScenario,
|
|
1620
2040
|
intersects,
|
|
1621
2041
|
ladderTrace,
|
|
1622
2042
|
makeAxisBrush,
|
|
@@ -1626,14 +2046,24 @@ async function captureGameState(page) {
|
|
|
1626
2046
|
makeLeafModel,
|
|
1627
2047
|
makeNode,
|
|
1628
2048
|
makePlane,
|
|
2049
|
+
serializeUserInfo,
|
|
1629
2050
|
setupBrowserEnvironment,
|
|
1630
2051
|
setupMockAudioContext,
|
|
1631
2052
|
setupNodeEnvironment,
|
|
1632
2053
|
simulateFrames,
|
|
1633
2054
|
simulateFramesWithMock,
|
|
2055
|
+
simulateHandshake,
|
|
2056
|
+
simulateNetworkCondition,
|
|
2057
|
+
simulatePlayerInput,
|
|
2058
|
+
simulatePlayerJoin,
|
|
2059
|
+
simulatePlayerLeave,
|
|
2060
|
+
simulateServerTick,
|
|
2061
|
+
simulateSnapshotDelivery,
|
|
1634
2062
|
stairTrace,
|
|
1635
2063
|
teardownBrowserEnvironment,
|
|
1636
2064
|
teardownMockAudioContext,
|
|
2065
|
+
throttleBandwidth,
|
|
2066
|
+
verifySnapshotConsistency,
|
|
1637
2067
|
waitForGameReady
|
|
1638
2068
|
});
|
|
1639
2069
|
//# sourceMappingURL=index.cjs.map
|