From 7276f4feced813289ccd3879eeda9d30a45aa2fa Mon Sep 17 00:00:00 2001 From: alshum Date: Mon, 30 Sep 2024 14:51:14 -0700 Subject: [PATCH 1/4] Add unit tests for regex filter --- .../web/src/SplunkErrorInstrumentation.ts | 2 +- packages/web/test/index.ts | 1 + packages/web/test/stacktrace.test.ts | 223 ++++++++++++++++++ packages/web/test/utils.ts | 19 ++ 4 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 packages/web/test/stacktrace.test.ts diff --git a/packages/web/src/SplunkErrorInstrumentation.ts b/packages/web/src/SplunkErrorInstrumentation.ts index fc51a84a..4e9a6950 100644 --- a/packages/web/src/SplunkErrorInstrumentation.ts +++ b/packages/web/src/SplunkErrorInstrumentation.ts @@ -40,7 +40,7 @@ function stringifyValue(value: unknown) { function parseErrorStack(stack: string): string { //get list of files in stack , find corresponding sourcemap id and add it to the source map id object const sourceMapIds = {}; - const urlPattern = /(https?:\/\/[^\s]+\/[^\s:]+|\/[^\s:]+)/g; + const urlPattern = /([\w]+:\/\/[^\s\/]+\/[^\s?:#]+)/g; const urls = stack.match(urlPattern); if (urls) { urls.forEach(url => { diff --git a/packages/web/test/index.ts b/packages/web/test/index.ts index 9da7723e..5cbd08fb 100644 --- a/packages/web/test/index.ts +++ b/packages/web/test/index.ts @@ -31,3 +31,4 @@ import './SplunkSpanAttributesProcessor.test'; import './SplunkOtelWeb.test'; import './synthetics.test'; import './socketio.test'; +import './stackTrace.test'; diff --git a/packages/web/test/stacktrace.test.ts b/packages/web/test/stacktrace.test.ts new file mode 100644 index 00000000..5e55d3ed --- /dev/null +++ b/packages/web/test/stacktrace.test.ts @@ -0,0 +1,223 @@ +/* +Copyright 2024 Splunk Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as assert from 'assert'; +import { beforeEach } from "mocha"; +import { generateFilePaths, generateRandomStackTrace } from "./utils"; + +const chromeStackTraceEval = `Error: Something went wrong + at eval (eval at (http://example.com/scripts/main.js:10:20), :1:1) + at Object.functionName (http://example.com/scripts/utils.js:15:25) + at http://example.com/scripts/app.js:20:30 + at new ConstructorName (http://example.com/scripts/controller.js:25:35) + at http://example.com/scripts/main.js:30:40`; +const chromeStackTraceEvalExpected = [ + "http://example.com/scripts/main.js", + "http://example.com/scripts/utils.js", + "http://example.com/scripts/app.js", + "http://example.com/scripts/controller.js", +]; + +const chromeStackTraceAnonymous = `TypeError: undefined is not a function + at http://example.com/js/anonymous.js:10:5 + at :15:10 + at Object.functionName (http://example.com/js/utils.js:20:15) + at new ConstructorName (http://example.com/js/app.js:25:20) + at :30:25`; +const chromeStackTraceAnonymousExpected = [ + "http://example.com/js/anonymous.js", + "http://example.com/js/utils.js", + "http://example.com/js/app.js", +]; + +const geckoStackTraceEval = `Error: Something went wrong + @http://example.com/scripts/main.js:10:20 + @eval (eval at :1:1) + functionName@http://example.com/scripts/utils.js:15:25 + @http://example.com/scripts/app.js:20:30 + ConstructorName@http://example.com/scripts/controller.js:25:35 + @http://example.com/scripts/main.js:30:40`; +const geckoStackTraceEvalExpected = [ + "http://example.com/scripts/main.js", + "http://example.com/scripts/utils.js", + "http://example.com/scripts/app.js", + "http://example.com/scripts/controller.js", +]; + +const geckoStackTraceAnonymous = `TypeError: undefined is not a function + @http://example.com/js/anonymous.js:10:5 + @:15:10 + functionName@http://example.com/js/utils.js:20:15 + ConstructorName@http://example.com/js/app.js:25:20 + @:30:25`; +const geckoStackTraceAnonymousExpected = [ + "http://example.com/js/anonymous.js", + "http://example.com/js/utils.js", + "http://example.com/js/app.js" +]; + +// Test 1: simple test w/ dupes +const stack1 = `Error + at http://localhost:8080/js/script1.js:10:15 + at http://localhost:8080/js/script2.js:20:25 + at http://localhost:8080/js/script1.js:30:35`; +const expected1 = ['http://localhost:8080/js/script1.js', 'http://localhost:8080/js/script2.js']; + +// Test 2: http and https +const stack2 = `Error + at https://example.com/js/app.js:50:10 + at http://localhost/js/util.js:100:50`; +const expected2 = ['https://example.com/js/app.js', 'http://localhost/js/util.js']; + +// Test 3: No full path URLs +const stack3 = `Error + at someFunction (file.js:10:15) + at anotherFunction (file.js:20:25)`; +const expected3 = []; + +// Test 4: Only one URL, with port +const stack4 = `Error + at http://localhost:3000/js/main.js:10:15`; +const expected4 = ['http://localhost:3000/js/main.js']; + +// Test 5: Duplicate URLs +const stack5 = `Error + at http://localhost:3000/js/main.js:10:15 + at http://localhost:3000/js/main.js:20:25 + at http://localhost:3000/js/utils.js:30:35`; +const expected5 = ['http://localhost:3000/js/main.js', 'http://localhost:3000/js/utils.js']; + +// Test 6: Urls with query strings and fragments +const stack6 = `Error + at http://example.com:8080/path/js/main.js?name=testname:10:15 + at http://example.com:8080/path/js/main2.js#fragmentHere:20:15 + at http://example.com:8080/path/js/main3.js?name=testname#fragmentHere:30:15`; +const expected6 = ['http://example.com:8080/path/js/main.js', 'http://example.com:8080/path/js/main2.js', 'http://example.com:8080/path/js/main3.js']; + +// Test 7: Urls with different protocols and blobs +const stack7 = `Error + at file://testing.com:8000/js/testFile.js:1:2 + at blob:https://example.com:1000/src/hello.js:2:3`; +const expected7 = ['file://testing.com:8000/js/testFile.js', 'https://example.com:1000/src/hello.js'] + +const regexFilter = /([\w]+:\/\/[^\s\/]+\/[^\s?:#]+)/g; +describe('regexFilter', () => { + let urls = new Set(); + let match; + + beforeEach(() => { + urls = new Set(); + match = null; + }); + it('should test chrome eval stack traces', () => { + while ((match = regexFilter.exec(chromeStackTraceEval)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceEvalExpected); + }); + + it ('should test chrome anonymous stack traces', () => { + while ((match = regexFilter.exec(chromeStackTraceAnonymous)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceAnonymousExpected); + }); + + it ('should test gecko eval stack traces', () => { + while ((match = regexFilter.exec(geckoStackTraceEval)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceEvalExpected); + }); + + it ('should test gecko anonymous stack traces', () => { + while ((match = regexFilter.exec(geckoStackTraceAnonymous)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceAnonymousExpected); + }); + + it ('should test simple stack trace with dupes', () => { + while ((match = regexFilter.exec(stack1)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected1); + }); + + it ('should test http vs https stack traces', () => { + while ((match = regexFilter.exec(stack2)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected2); + }); + + it ('should test no full url path stack traces', () => { + while ((match = regexFilter.exec(stack3)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected3); + }); + + it ('should test url ports in stack traces', () => { + while ((match = regexFilter.exec(stack4)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected4); + }); + + it ('should test duplicate urls in stack traces', () => { + while ((match = regexFilter.exec(stack5)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected5); + }); + + it ('should test query strings/fragments in stack traces', () => { + while ((match = regexFilter.exec(stack6)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected6); + }); + + it ('should test blobs and diff protocols in stack traces', () => { + while ((match = regexFilter.exec(stack7)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr, expected7); + }); + + it ('should test long stack traces', () => { + const randomPaths = generateFilePaths(20, 20); + const randomStack = generateRandomStackTrace(randomPaths, 10000); + + while ((match = regexFilter.exec(randomStack)) !== null) { + urls.add(match[0]); + } + let urlArr = [...urls]; + assert.deepEqual(urlArr.sort(), randomPaths.sort()); + }); +}); \ No newline at end of file diff --git a/packages/web/test/utils.ts b/packages/web/test/utils.ts index 578bf0d8..14f31285 100644 --- a/packages/web/test/utils.ts +++ b/packages/web/test/utils.ts @@ -99,3 +99,22 @@ export function initWithSyncPipeline(additionalOptions = {}): { export function deinit(force?: boolean): void { SplunkRum.deinit(force); } + +export function generateFilePaths(domainCount: number, pathCount: number) { + const paths: string[] = []; + for (let i = 0; i < domainCount; i++) { + let domain = `http://domain${i}.com`; + for (let j = 0; j < pathCount; j++) { + paths.push(`${domain}/path${j}.js`); + } + } + return paths; +} + +export function generateRandomStackTrace(paths: string[], stackCount: number) { + let stack = 'Error\n'; + for (let i = 0; i < stackCount; i++) { + stack += `at ${paths[Math.floor(Math.random() * paths.length)]}:${Math.floor(Math.random() * 1000)}:${Math.floor(Math.random() * 1000)}\n`; + } + return stack; +} From 734564cf573ceaa9e17b5ebca87d2952a122bfbc Mon Sep 17 00:00:00 2001 From: Alan Shum Date: Mon, 30 Sep 2024 17:44:09 -0700 Subject: [PATCH 2/4] fix karma tests not running --- packages/web/test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/test/index.ts b/packages/web/test/index.ts index 5cbd08fb..d90bafe4 100644 --- a/packages/web/test/index.ts +++ b/packages/web/test/index.ts @@ -31,4 +31,4 @@ import './SplunkSpanAttributesProcessor.test'; import './SplunkOtelWeb.test'; import './synthetics.test'; import './socketio.test'; -import './stackTrace.test'; +import './stacktrace.test'; From d7e4789389177e2c8b9268636ee40f756bd01092 Mon Sep 17 00:00:00 2001 From: Abinet Debele Date: Mon, 30 Sep 2024 10:21:18 -0700 Subject: [PATCH 3/4] Rebase and sign commits --- packages/web/src/SplunkErrorInstrumentation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/SplunkErrorInstrumentation.ts b/packages/web/src/SplunkErrorInstrumentation.ts index 4e9a6950..fc51a84a 100644 --- a/packages/web/src/SplunkErrorInstrumentation.ts +++ b/packages/web/src/SplunkErrorInstrumentation.ts @@ -40,7 +40,7 @@ function stringifyValue(value: unknown) { function parseErrorStack(stack: string): string { //get list of files in stack , find corresponding sourcemap id and add it to the source map id object const sourceMapIds = {}; - const urlPattern = /([\w]+:\/\/[^\s\/]+\/[^\s?:#]+)/g; + const urlPattern = /(https?:\/\/[^\s]+\/[^\s:]+|\/[^\s:]+)/g; const urls = stack.match(urlPattern); if (urls) { urls.forEach(url => { From 27f07f27ec4f229427c0d6bbfec516ac5b1d4395 Mon Sep 17 00:00:00 2001 From: Alan Shum Date: Wed, 2 Oct 2024 01:59:16 -0700 Subject: [PATCH 4/4] Fixed eslint issues --- .../web/src/SplunkErrorInstrumentation.ts | 2 +- packages/web/test/stacktrace.test.ts | 246 +++++++++--------- packages/web/test/utils.ts | 6 +- 3 files changed, 127 insertions(+), 127 deletions(-) diff --git a/packages/web/src/SplunkErrorInstrumentation.ts b/packages/web/src/SplunkErrorInstrumentation.ts index fc51a84a..d71d0616 100644 --- a/packages/web/src/SplunkErrorInstrumentation.ts +++ b/packages/web/src/SplunkErrorInstrumentation.ts @@ -40,7 +40,7 @@ function stringifyValue(value: unknown) { function parseErrorStack(stack: string): string { //get list of files in stack , find corresponding sourcemap id and add it to the source map id object const sourceMapIds = {}; - const urlPattern = /(https?:\/\/[^\s]+\/[^\s:]+|\/[^\s:]+)/g; + const urlPattern = /([\w]+:\/\/[^\s/]+\/[^\s?:#]+)/g; const urls = stack.match(urlPattern); if (urls) { urls.forEach(url => { diff --git a/packages/web/test/stacktrace.test.ts b/packages/web/test/stacktrace.test.ts index 5e55d3ed..7dc6f9f1 100644 --- a/packages/web/test/stacktrace.test.ts +++ b/packages/web/test/stacktrace.test.ts @@ -15,8 +15,8 @@ limitations under the License. */ import * as assert from 'assert'; -import { beforeEach } from "mocha"; -import { generateFilePaths, generateRandomStackTrace } from "./utils"; +import { beforeEach } from 'mocha'; +import { generateFilePaths, generateRandomStackTrace } from './utils'; const chromeStackTraceEval = `Error: Something went wrong at eval (eval at (http://example.com/scripts/main.js:10:20), :1:1) @@ -25,10 +25,10 @@ const chromeStackTraceEval = `Error: Something went wrong at new ConstructorName (http://example.com/scripts/controller.js:25:35) at http://example.com/scripts/main.js:30:40`; const chromeStackTraceEvalExpected = [ - "http://example.com/scripts/main.js", - "http://example.com/scripts/utils.js", - "http://example.com/scripts/app.js", - "http://example.com/scripts/controller.js", + 'http://example.com/scripts/main.js', + 'http://example.com/scripts/utils.js', + 'http://example.com/scripts/app.js', + 'http://example.com/scripts/controller.js', ]; const chromeStackTraceAnonymous = `TypeError: undefined is not a function @@ -38,9 +38,9 @@ const chromeStackTraceAnonymous = `TypeError: undefined is not a function at new ConstructorName (http://example.com/js/app.js:25:20) at :30:25`; const chromeStackTraceAnonymousExpected = [ - "http://example.com/js/anonymous.js", - "http://example.com/js/utils.js", - "http://example.com/js/app.js", + 'http://example.com/js/anonymous.js', + 'http://example.com/js/utils.js', + 'http://example.com/js/app.js', ]; const geckoStackTraceEval = `Error: Something went wrong @@ -51,10 +51,10 @@ const geckoStackTraceEval = `Error: Something went wrong ConstructorName@http://example.com/scripts/controller.js:25:35 @http://example.com/scripts/main.js:30:40`; const geckoStackTraceEvalExpected = [ - "http://example.com/scripts/main.js", - "http://example.com/scripts/utils.js", - "http://example.com/scripts/app.js", - "http://example.com/scripts/controller.js", + 'http://example.com/scripts/main.js', + 'http://example.com/scripts/utils.js', + 'http://example.com/scripts/app.js', + 'http://example.com/scripts/controller.js', ]; const geckoStackTraceAnonymous = `TypeError: undefined is not a function @@ -64,9 +64,9 @@ const geckoStackTraceAnonymous = `TypeError: undefined is not a function ConstructorName@http://example.com/js/app.js:25:20 @:30:25`; const geckoStackTraceAnonymousExpected = [ - "http://example.com/js/anonymous.js", - "http://example.com/js/utils.js", - "http://example.com/js/app.js" + 'http://example.com/js/anonymous.js', + 'http://example.com/js/utils.js', + 'http://example.com/js/app.js' ]; // Test 1: simple test w/ dupes @@ -111,113 +111,113 @@ const expected6 = ['http://example.com:8080/path/js/main.js', 'http://example.co const stack7 = `Error at file://testing.com:8000/js/testFile.js:1:2 at blob:https://example.com:1000/src/hello.js:2:3`; -const expected7 = ['file://testing.com:8000/js/testFile.js', 'https://example.com:1000/src/hello.js'] +const expected7 = ['file://testing.com:8000/js/testFile.js', 'https://example.com:1000/src/hello.js']; -const regexFilter = /([\w]+:\/\/[^\s\/]+\/[^\s?:#]+)/g; +const regexFilter = /([\w]+:\/\/[^\s/]+\/[^\s?:#]+)/g; describe('regexFilter', () => { - let urls = new Set(); - let match; - - beforeEach(() => { - urls = new Set(); - match = null; - }); - it('should test chrome eval stack traces', () => { - while ((match = regexFilter.exec(chromeStackTraceEval)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, chromeStackTraceEvalExpected); - }); - - it ('should test chrome anonymous stack traces', () => { - while ((match = regexFilter.exec(chromeStackTraceAnonymous)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, chromeStackTraceAnonymousExpected); - }); - - it ('should test gecko eval stack traces', () => { - while ((match = regexFilter.exec(geckoStackTraceEval)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, chromeStackTraceEvalExpected); - }); - - it ('should test gecko anonymous stack traces', () => { - while ((match = regexFilter.exec(geckoStackTraceAnonymous)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, chromeStackTraceAnonymousExpected); - }); - - it ('should test simple stack trace with dupes', () => { - while ((match = regexFilter.exec(stack1)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected1); - }); - - it ('should test http vs https stack traces', () => { - while ((match = regexFilter.exec(stack2)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected2); - }); - - it ('should test no full url path stack traces', () => { - while ((match = regexFilter.exec(stack3)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected3); - }); - - it ('should test url ports in stack traces', () => { - while ((match = regexFilter.exec(stack4)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected4); - }); - - it ('should test duplicate urls in stack traces', () => { - while ((match = regexFilter.exec(stack5)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected5); - }); - - it ('should test query strings/fragments in stack traces', () => { - while ((match = regexFilter.exec(stack6)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected6); - }); - - it ('should test blobs and diff protocols in stack traces', () => { - while ((match = regexFilter.exec(stack7)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr, expected7); - }); - - it ('should test long stack traces', () => { - const randomPaths = generateFilePaths(20, 20); - const randomStack = generateRandomStackTrace(randomPaths, 10000); - - while ((match = regexFilter.exec(randomStack)) !== null) { - urls.add(match[0]); - } - let urlArr = [...urls]; - assert.deepEqual(urlArr.sort(), randomPaths.sort()); - }); + let urls = new Set(); + let match; + + beforeEach(() => { + urls = new Set(); + match = null; + }); + it('should test chrome eval stack traces', () => { + while ((match = regexFilter.exec(chromeStackTraceEval)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceEvalExpected); + }); + + it ('should test chrome anonymous stack traces', () => { + while ((match = regexFilter.exec(chromeStackTraceAnonymous)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, chromeStackTraceAnonymousExpected); + }); + + it ('should test gecko eval stack traces', () => { + while ((match = regexFilter.exec(geckoStackTraceEval)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, geckoStackTraceEvalExpected); + }); + + it ('should test gecko anonymous stack traces', () => { + while ((match = regexFilter.exec(geckoStackTraceAnonymous)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, geckoStackTraceAnonymousExpected); + }); + + it ('should test simple stack trace with dupes', () => { + while ((match = regexFilter.exec(stack1)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected1); + }); + + it ('should test http vs https stack traces', () => { + while ((match = regexFilter.exec(stack2)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected2); + }); + + it ('should test no full url path stack traces', () => { + while ((match = regexFilter.exec(stack3)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected3); + }); + + it ('should test url ports in stack traces', () => { + while ((match = regexFilter.exec(stack4)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected4); + }); + + it ('should test duplicate urls in stack traces', () => { + while ((match = regexFilter.exec(stack5)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected5); + }); + + it ('should test query strings/fragments in stack traces', () => { + while ((match = regexFilter.exec(stack6)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected6); + }); + + it ('should test blobs and diff protocols in stack traces', () => { + while ((match = regexFilter.exec(stack7)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr, expected7); + }); + + it ('should test long stack traces', () => { + const randomPaths = generateFilePaths(20, 20); + const randomStack = generateRandomStackTrace(randomPaths, 10000); + + while ((match = regexFilter.exec(randomStack)) !== null) { + urls.add(match[0]); + } + const urlArr = [...urls]; + assert.deepEqual(urlArr.sort(), randomPaths.sort()); + }); }); \ No newline at end of file diff --git a/packages/web/test/utils.ts b/packages/web/test/utils.ts index 14f31285..540a3c19 100644 --- a/packages/web/test/utils.ts +++ b/packages/web/test/utils.ts @@ -100,10 +100,10 @@ export function deinit(force?: boolean): void { SplunkRum.deinit(force); } -export function generateFilePaths(domainCount: number, pathCount: number) { +export function generateFilePaths(domainCount: number, pathCount: number): string[] { const paths: string[] = []; for (let i = 0; i < domainCount; i++) { - let domain = `http://domain${i}.com`; + const domain = `http://domain${i}.com`; for (let j = 0; j < pathCount; j++) { paths.push(`${domain}/path${j}.js`); } @@ -111,7 +111,7 @@ export function generateFilePaths(domainCount: number, pathCount: number) { return paths; } -export function generateRandomStackTrace(paths: string[], stackCount: number) { +export function generateRandomStackTrace(paths: string[], stackCount: number): string { let stack = 'Error\n'; for (let i = 0; i < stackCount; i++) { stack += `at ${paths[Math.floor(Math.random() * paths.length)]}:${Math.floor(Math.random() * 1000)}:${Math.floor(Math.random() * 1000)}\n`;