@w-lfpup/jackrabbit 0.3.0 → 0.3.1

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.
@@ -2,9 +2,7 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { testHanger } from "./test_hangar.js";
4
4
  let cwd = process.cwd();
5
- // better done with URL? feels weird
6
- let corePath = path.join(import.meta.url.substring(5), "../../../core/dist/");
7
- let browserPath = path.join(import.meta.url.substring(5), "../../../browser/dist/");
5
+ const parentPath = path.join(import.meta.url.substring(5), "../../../");
8
6
  const MIME_TYPES = {
9
7
  octet: "application/octet-stream",
10
8
  html: "text/html; charset=UTF-8",
@@ -107,33 +105,27 @@ function logAction(req, res, eventbus) {
107
105
  }
108
106
  async function serveFile(req, res) {
109
107
  let { url, method } = req;
110
- if (!url) {
108
+ if (!url || "GET" !== method) {
111
109
  res.setHeader("Content-Type", MIME_TYPES["html"]);
112
110
  res.writeHead(400);
113
111
  res.end();
114
112
  return;
115
113
  }
116
- let ext = "";
114
+ let urlFilePath = path.join(url);
115
+ let extStr = "";
117
116
  if (url.endsWith("/"))
118
- ext = "index.html";
117
+ extStr = "index.html";
119
118
  let urlNoPrefix = url;
120
- if (url.startsWith("/jackrabbit"))
121
- urlNoPrefix = url.substring(11);
122
- let filePath = path.join(cwd, urlNoPrefix, ext);
123
- let stream;
124
- if (url.startsWith("/jackrabbit/core/") && "GET" === method) {
125
- stream = await getDirectoryScopedFile(filePath, corePath);
119
+ if (urlFilePath.startsWith("/jackrabbit")) {
120
+ let strippedUrl = urlFilePath.substring("/jackrabbit".length);
121
+ urlFilePath = path.join(parentPath, strippedUrl, extStr);
126
122
  }
127
- if (url.startsWith("/jackrabbit/browser/") && "GET" === method) {
128
- stream = await getDirectoryScopedFile(filePath, browserPath);
129
- }
130
- if (!url.startsWith("/jackrabbit") && "GET" === method) {
131
- stream = await getDirectoryScopedFile(filePath, cwd);
123
+ else {
124
+ urlFilePath = path.join(cwd, urlNoPrefix, extStr);
132
125
  }
126
+ let stream = await getFile(urlFilePath);
133
127
  if (stream) {
134
- // throws errors if not a string
135
- // filepath is always a string
136
- const ext = path.extname(filePath).substring(1).toLowerCase();
128
+ const ext = path.extname(urlFilePath).substring(1).toLowerCase();
137
129
  let mimeType = MIME_TYPES[ext] ?? MIME_TYPES["octet"];
138
130
  res.setHeader("Content-Type", mimeType);
139
131
  res.writeHead(200);
@@ -161,9 +153,7 @@ function getLoggerActionFromRequestBody(req) {
161
153
  });
162
154
  });
163
155
  }
164
- async function getDirectoryScopedFile(filePath, basePath) {
165
- if (!filePath.startsWith(basePath))
166
- return;
156
+ async function getFile(filePath) {
167
157
  try {
168
158
  await fs.promises.access(filePath);
169
159
  return fs.createReadStream(filePath);
@@ -134,14 +134,14 @@ function setupProcess(params, eventbus, externalSignal) {
134
134
  });
135
135
  }
136
136
  });
137
- process.addListener("error", (error) => {
137
+ process.addListener("error", function (error) {
138
138
  eventbus.dispatchAction({
139
139
  id: jrId,
140
140
  type: "session_error",
141
141
  error: error.toString(),
142
142
  });
143
143
  });
144
- process.addListener("exit", (statusCode) => {
144
+ process.addListener("exit", function (statusCode) {
145
145
  if (statusCode) {
146
146
  eventbus.dispatchAction({
147
147
  type: "session_error",
@@ -219,7 +219,6 @@ async function setCookie(params, signal, sessionId) {
219
219
  cookie: {
220
220
  name: "jackrabbit",
221
221
  value: jrId,
222
- // domain: this.#hostAndPort (issues in firefox)
223
222
  path: "/",
224
223
  httpOnly: true,
225
224
  },
@@ -7,7 +7,7 @@ import type {
7
7
  } from "./eventbus.js";
8
8
  import type { SessionResults, RunResults } from "./results.js";
9
9
 
10
- import { getResultsAsString } from "./results_str.js";
10
+ import { getResultsAsString, isComplete } from "./results.js";
11
11
 
12
12
  export class Logger {
13
13
  #eventbus: EventBus;
@@ -51,8 +51,8 @@ export class Logger {
51
51
  return this.#sessionResults.errors !== 0;
52
52
  }
53
53
 
54
- get compeleted() {
55
- return false;
54
+ get completed() {
55
+ return isComplete(this.#sessionResults);
56
56
  }
57
57
 
58
58
  get results(): string {
@@ -25,7 +25,9 @@ let server = http.createServer();
25
25
  server.addListener("request", router.route);
26
26
  server.addListener("close", function () {
27
27
  console.log(logger.results);
28
- logger.errored || logger.failed ? process.exit(1) : process.exit(0);
28
+ logger.errored || logger.failed || !logger.completed
29
+ ? process.exit(1)
30
+ : process.exit(0);
29
31
  });
30
32
  eventbus.addListener("end", function () {
31
33
  server.closeAllConnections();
@@ -7,7 +7,6 @@ export interface TestResults {
7
7
  loggerEndAction: LoggerAction | undefined;
8
8
  }
9
9
 
10
- // remove logger actions
11
10
  export interface ModuleResults {
12
11
  loggerAction: LoggerAction;
13
12
  fails: number;
@@ -47,10 +46,266 @@ export interface RunResults {
47
46
  collections: (CollectionResults | undefined)[];
48
47
  }
49
48
 
50
- // track if session started or not
51
- // if browser never launched
52
49
  export interface SessionResults {
53
50
  fails: number;
54
51
  errors: number;
55
52
  runs: Map<string, RunResults>;
56
53
  }
54
+
55
+ const SPACE = " ";
56
+
57
+ /*
58
+ Lots of nested loops because results a nested structure.
59
+ I'd rather see composition nested in one function
60
+ than have for loops spread across each function.
61
+ */
62
+
63
+ export function getResultsAsString(sessionResults: SessionResults): string {
64
+ const output: string[] = [];
65
+
66
+ logSessionErrors(output, sessionResults);
67
+
68
+ for (let [, result] of sessionResults.runs) {
69
+ if (logRunResults(output, result)) continue;
70
+
71
+ for (const collection of result.collections) {
72
+ if (logCollectionResult(output, collection)) continue;
73
+
74
+ if (collection)
75
+ for (const moduleResult of collection.modules) {
76
+ if (logModuleResult(output, moduleResult)) continue;
77
+
78
+ if (moduleResult)
79
+ for (const testResult of moduleResult.testResults) {
80
+ logTest(output, testResult);
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ logSummary(output, sessionResults);
87
+
88
+ return output.join("\n");
89
+ }
90
+
91
+ function logSessionErrors(output: string[], sessionResults: SessionResults) {
92
+ if (!sessionResults.runs.size) output.push("\nNo webdrivers were run.");
93
+
94
+ for (let [, result] of sessionResults.runs) {
95
+ if (result.errorLogs.length)
96
+ output.push(`\n${result.webdriverParams.title}`);
97
+ for (let errorAction of result.errorLogs) {
98
+ if ("session_error" === errorAction.type) {
99
+ output.push(`${SPACE}[session_error] ${errorAction.error}`);
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ function logRunResults(output: string[], result: RunResults): boolean {
106
+ output.push(`\n${result.webdriverParams.title}`);
107
+
108
+ for (let errorAction of result.errorLogs) {
109
+ if ("log" !== errorAction.type) continue;
110
+ if ("run_error" !== errorAction.loggerAction.type) continue;
111
+
112
+ output.push(
113
+ `${SPACE.repeat(2)}[run_error] ${errorAction.loggerAction.error}`,
114
+ );
115
+ }
116
+ if (result.errorLogs.length) output.push("");
117
+
118
+ if (!result.collections && !result.expectedTests) {
119
+ output.push(`${SPACE}No tests were run.`);
120
+ return true;
121
+ }
122
+
123
+ // When everything goes right :3
124
+ if (
125
+ !result.fails &&
126
+ !result.errors &&
127
+ result.expectedTests === result.completedTests &&
128
+ result.expectedModules === result.completedModules &&
129
+ result.expectedCollections === result.completedCollections
130
+ ) {
131
+ output.push(`${SPACE}${result.completedTests} tests
132
+ ${SPACE}${result.completedModules} modules
133
+ ${SPACE}${result.completedCollections} collections`);
134
+ return true;
135
+ }
136
+
137
+ return false;
138
+ }
139
+
140
+ function logCollectionResult(
141
+ output: string[],
142
+ collection: CollectionResults | undefined,
143
+ ): boolean {
144
+ if (!collection) return true;
145
+
146
+ let { loggerAction } = collection;
147
+ if ("start_collection" !== loggerAction.type) return true;
148
+
149
+ output.push(`${SPACE}${loggerAction.collection_url}`);
150
+
151
+ // when everything in the collection goes right
152
+ if (
153
+ !collection.fails &&
154
+ !collection.errors &&
155
+ collection.expectedTests === collection.completedTests &&
156
+ collection.expectedModules === collection.completedModules
157
+ ) {
158
+ output.push(
159
+ `${SPACE.repeat(2)}${collection.expectedTests} tests
160
+ ${SPACE.repeat(2)}${loggerAction.expected_module_count} modules`,
161
+ );
162
+
163
+ return true;
164
+ }
165
+
166
+ for (let errorAction of collection.errorLogs) {
167
+ if ("collection_error" !== errorAction.type) continue;
168
+ output.push(`${SPACE.repeat(2)}[collection_error] ${errorAction.error}`);
169
+ }
170
+
171
+ if (collection.errorLogs.length) output.push("");
172
+
173
+ return false;
174
+ }
175
+
176
+ function logModuleResult(
177
+ output: string[],
178
+ module: ModuleResults | undefined,
179
+ ): boolean {
180
+ if (!module) return true;
181
+
182
+ let { loggerAction } = module;
183
+ if ("start_module" !== loggerAction.type) return true;
184
+
185
+ output.push(`${SPACE.repeat(2)}${loggerAction.module_name}`);
186
+
187
+ // when everything in the module goes right
188
+ if (
189
+ !module.fails &&
190
+ !module.errors &&
191
+ module.expectedTests === module.completedTests
192
+ ) {
193
+ output.push(`${SPACE.repeat(3)}${module.expectedTests} tests`);
194
+ return true;
195
+ }
196
+
197
+ output.push(
198
+ `${SPACE.repeat(3)}${module.completedTests - module.fails}/${module.expectedTests} tests`,
199
+ );
200
+
201
+ output.push("");
202
+ for (let errorAction of module.errorLogs) {
203
+ if ("module_error" !== errorAction.type) continue;
204
+ output.push(`${SPACE.repeat(3)}[module_error] ${errorAction.error}`);
205
+ }
206
+
207
+ if (module.errorLogs.length) output.push("");
208
+
209
+ return false;
210
+ }
211
+
212
+ function logTest(output: string[], test: TestResults | undefined) {
213
+ if (!test) return;
214
+
215
+ let { loggerStartAction, loggerEndAction } = test;
216
+ if ("start_test" !== loggerStartAction.type) return;
217
+
218
+ if ("test_error" === loggerEndAction?.type) {
219
+ let { test_name } = loggerStartAction;
220
+ output.push(
221
+ `${SPACE.repeat(3)}${test_name}
222
+ ${SPACE.repeat(4)}[error] ${loggerEndAction.error}`,
223
+ );
224
+ }
225
+
226
+ if ("end_test" === loggerEndAction?.type) {
227
+ let { assertions } = loggerEndAction;
228
+ const isAssertionArray = Array.isArray(assertions) && assertions.length;
229
+ const isAssertion =
230
+ !Array.isArray(assertions) &&
231
+ undefined !== assertions &&
232
+ null !== assertions;
233
+
234
+ if (isAssertion || isAssertionArray) {
235
+ let { test_name } = loggerStartAction;
236
+ output.push(`${SPACE.repeat(3)}${test_name}`);
237
+ }
238
+
239
+ if (isAssertion) {
240
+ output.push(`${SPACE.repeat(4)}- ${assertions}`);
241
+ }
242
+
243
+ if (isAssertionArray) {
244
+ for (const assertion of assertions) {
245
+ output.push(`${SPACE.repeat(4)}- ${assertion}`);
246
+ }
247
+ }
248
+ }
249
+
250
+ if (undefined === loggerEndAction) {
251
+ let { test_name } = loggerStartAction;
252
+
253
+ output.push(`${SPACE.repeat(3)}${test_name}
254
+ ${SPACE.repeat(4)}[incomplete]`);
255
+ }
256
+ }
257
+
258
+ function logSummary(output: string[], sessionResults: SessionResults) {
259
+ let status_with_color = blue("\u{2714} passed");
260
+
261
+ if (!isComplete(sessionResults))
262
+ status_with_color = gray("\u{2717} incomplete");
263
+ if (sessionResults.fails) status_with_color = yellow("\u{2717} failed");
264
+ if (sessionResults.errors) status_with_color = gray("\u{2717} errored");
265
+
266
+ let totalTime = 0;
267
+ let testTime = 0;
268
+ for (let [, run] of sessionResults.runs) {
269
+ totalTime += run.endTime - run.startTime;
270
+ testTime += run.testTime;
271
+ }
272
+
273
+ output.push(`
274
+ ${status_with_color}
275
+ duration: ${testTime.toFixed(4)} mS
276
+ total: ${totalTime.toFixed(4)} mS
277
+ `);
278
+ }
279
+
280
+ export function isComplete(sessionResults: SessionResults): boolean {
281
+ for (const [, result] of sessionResults.runs) {
282
+ if (
283
+ !result.expectedTests ||
284
+ !result.expectedModules ||
285
+ !result.expectedCollections
286
+ )
287
+ return false;
288
+ if (
289
+ result.expectedTests !== result.completedTests ||
290
+ result.expectedModules !== result.completedModules ||
291
+ result.expectedCollections !== result.completedCollections
292
+ )
293
+ return false;
294
+ }
295
+
296
+ return true;
297
+ }
298
+
299
+ // 39 - default foreground color
300
+ // 49 - default background color
301
+ function blue(text: string) {
302
+ return `\x1b[44m\x1b[97m${text}\x1b[0m`;
303
+ }
304
+
305
+ function yellow(text: string) {
306
+ return `\x1b[43m\x1b[97m${text}\x1b[0m`;
307
+ }
308
+
309
+ function gray(text: string) {
310
+ return `\x1b[100m\x1b[97m${text}\x1b[0m`;
311
+ }
@@ -8,13 +8,7 @@ import { testHanger } from "./test_hangar.js";
8
8
  import { ConfigInterface } from "./config.js";
9
9
 
10
10
  let cwd = process.cwd();
11
-
12
- // better done with URL? feels weird
13
- let corePath = path.join(import.meta.url.substring(5), "../../../core/dist/");
14
- let browserPath = path.join(
15
- import.meta.url.substring(5),
16
- "../../../browser/dist/",
17
- );
11
+ const parentPath = path.join(import.meta.url.substring(5), "../../../");
18
12
 
19
13
  const MIME_TYPES: Record<string, string> = {
20
14
  octet: "application/octet-stream",
@@ -138,35 +132,33 @@ function logAction(
138
132
  async function serveFile(req: IncomingMessage, res: ServerResponse) {
139
133
  let { url, method } = req;
140
134
 
141
- if (!url) {
135
+ if (!url || "GET" !== method) {
142
136
  res.setHeader("Content-Type", MIME_TYPES["html"]);
143
137
  res.writeHead(400);
144
138
  res.end();
145
139
  return;
146
140
  }
147
141
 
148
- let ext = "";
149
- if (url.endsWith("/")) ext = "index.html";
142
+ // assume http 1.1
143
+ let urlFilePath = path.join(url);
144
+
145
+ let extStr = "";
146
+ if (url.endsWith("/")) extStr = "index.html";
150
147
  let urlNoPrefix = url;
151
- if (url.startsWith("/jackrabbit")) urlNoPrefix = url.substring(11);
152
- let filePath = path.join(cwd, urlNoPrefix, ext);
153
148
 
154
- let stream: fs.ReadStream | undefined;
155
- if (url.startsWith("/jackrabbit/core/") && "GET" === method) {
156
- stream = await getDirectoryScopedFile(filePath, corePath);
157
- }
158
- if (url.startsWith("/jackrabbit/browser/") && "GET" === method) {
159
- stream = await getDirectoryScopedFile(filePath, browserPath);
149
+ if (urlFilePath.startsWith("/jackrabbit")) {
150
+ let strippedUrl = urlFilePath.substring("/jackrabbit".length);
151
+ urlFilePath = path.join(parentPath, strippedUrl, extStr);
152
+ } else {
153
+ urlFilePath = path.join(cwd, urlNoPrefix, extStr);
160
154
  }
161
155
 
162
- if (!url.startsWith("/jackrabbit") && "GET" === method) {
163
- stream = await getDirectoryScopedFile(filePath, cwd);
164
- }
156
+ let stream = await getFile(urlFilePath);
165
157
 
166
158
  if (stream) {
167
159
  // throws errors if not a string
168
160
  // filepath is always a string
169
- const ext = path.extname(filePath).substring(1).toLowerCase();
161
+ const ext = path.extname(urlFilePath).substring(1).toLowerCase();
170
162
  let mimeType = MIME_TYPES[ext] ?? MIME_TYPES["octet"];
171
163
  res.setHeader("Content-Type", mimeType);
172
164
  res.writeHead(200);
@@ -198,12 +190,7 @@ function getLoggerActionFromRequestBody(
198
190
  });
199
191
  }
200
192
 
201
- async function getDirectoryScopedFile(
202
- filePath: string,
203
- basePath: string,
204
- ): Promise<fs.ReadStream | undefined> {
205
- if (!filePath.startsWith(basePath)) return;
206
-
193
+ async function getFile(filePath: string): Promise<fs.ReadStream | undefined> {
207
194
  try {
208
195
  await fs.promises.access(filePath);
209
196
  return fs.createReadStream(filePath);
@@ -149,6 +149,7 @@ class WebdriverSession {
149
149
  this.#eventbus,
150
150
  this.#sessionId,
151
151
  );
152
+
152
153
  this.#process.kill();
153
154
  this.#process = undefined;
154
155
  }
@@ -195,14 +196,14 @@ function setupProcess(
195
196
  }
196
197
  },
197
198
  );
198
- process.addListener("error", (error) => {
199
+ process.addListener("error", function (error) {
199
200
  eventbus.dispatchAction({
200
201
  id: jrId,
201
202
  type: "session_error",
202
203
  error: error.toString(),
203
204
  });
204
205
  });
205
- process.addListener("exit", (statusCode) => {
206
+ process.addListener("exit", function (statusCode) {
206
207
  if (statusCode) {
207
208
  eventbus.dispatchAction({
208
209
  type: "session_error",
@@ -1 +1 @@
1
- {"root":["./src/config.ts","./src/eventbus.ts","./src/logger.ts","./src/mod.ts","./src/results.ts","./src/results_str.ts","./src/routes.ts","./src/test_hangar.ts","./src/webdriver.ts"],"version":"5.9.3"}
1
+ {"root":["./src/config.ts","./src/eventbus.ts","./src/logger.ts","./src/mod.ts","./src/results.ts","./src/routes.ts","./src/test_hangar.ts","./src/webdriver.ts"],"version":"5.9.3"}