Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TINY-11177: Vastly improve remote testing #145

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fb898e4
TINY-11177: Add an empty favicon to prevent the browser requesting `f…
TheSpyder Aug 16, 2024
56b5985
TINY-11177: Add keep-alive header to stop weird 502 bad gateway errors
TheSpyder Aug 16, 2024
e3e659f
TINY-11177: Send significantly less reports from the client. Don't wa…
TheSpyder Aug 16, 2024
57a42a4
TINY-11177: Reverted TINY-10708 which was a server-side fix. We neede…
TheSpyder Aug 16, 2024
ad4b260
TINY-11177: Re-implemented skip reports, we have enough of them that …
TheSpyder Aug 16, 2024
9ea95a3
TINY-11177: Updated reporter test for logical changes. Fixed a bug wi…
TheSpyder Aug 16, 2024
070c20a
TINY-11177: Removed @ephox/wrap-promise-polyfill
TheSpyder Aug 16, 2024
bcb63b7
TINY-11177: Rewriting the entire report system to support batch resul…
TheSpyder Sep 13, 2024
980c3b9
TINY-11177: Delete route that accepts a single test result
TheSpyder Sep 15, 2024
e80663d
TINY-11177: Changelog
TheSpyder Sep 15, 2024
625a2ce
TINY-11177: Fixed test
TheSpyder Sep 16, 2024
b0dc184
TINY-11177: Improved test failure reporting
TheSpyder Sep 16, 2024
c4359e8
TINY-11177: The test wasn't broken, my code was. Messages sent close …
TheSpyder Sep 16, 2024
5e55c44
TINY-11177: Only update the HUD once for batch results, instead of on…
TheSpyder Sep 16, 2024
7b0d367
TINY-11177: Setup remote testing
jscasca Sep 16, 2024
93d8408
TINY-11177: Fixed issue where it was not clear test failures must be …
TheSpyder Sep 16, 2024
0cbc33f
TINY-11177: Send start once, not once per page
TheSpyder Sep 16, 2024
0287158
TINY-11177: Fix HUD test count now that we are sending all results
TheSpyder Sep 16, 2024
5cd5c77
TINY-11177: Only record results if there are results
TheSpyder Sep 16, 2024
73b5391
TINY-11177: Add extra warning for duplicate tests
TheSpyder Sep 16, 2024
4094f6d
TINY-11177: Post current results before reloading the browser for eit…
TheSpyder Sep 16, 2024
07f61f8
TINY-11177: Fixed test waiting for done to decide to post results
TheSpyder Sep 16, 2024
f025e1d
alpha 3
TheSpyder Sep 16, 2024
801e908
TINY-11177: Warn for skipped tests as well
TheSpyder Sep 16, 2024
51533cd
TINY-11177: Revert failed attempt to fix sending the final result batch
TheSpyder Sep 16, 2024
40d2402
TINY-11177: Extend HUD limit to the terminal width. Disable the limit…
TheSpyder Sep 17, 2024
7778c70
TINY-11177: Reduce confusion with test reports and error messaging
TheSpyder Sep 17, 2024
eaab339
TINY-11177: Fail webdriver errors properly, don't generate new errors…
TheSpyder Sep 17, 2024
8afd4d5
TINY-11177: Alpha 4
TheSpyder Sep 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

## Fixed
- Reverted TINY-10708 which was a server-side fix
- Reduced logging from client to server, and stopped waiting for log requests to complete between tests #TINY-11177

## Removed
- The Promise polyfill is no longer allowed on modern NodeJS frameworks so it has been removed. #TINY-11177

## 14.1.4 - 2024-03-27

### Fixed
Expand Down
1 change: 0 additions & 1 deletion modules/runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
},
"dependencies": {
"@ephox/bedrock-common": "^14.1.1",
"@ephox/wrap-promise-polyfill": "^2.2.0",
"jquery": "^3.4.1",
"querystringify": "^2.1.1"
},
Expand Down
1 change: 0 additions & 1 deletion modules/runner/src/main/ts/api/Main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Failure, Global } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import * as Globals from '../core/Globals';
import * as TestLoader from '../core/TestLoader';
import { UrlParams } from '../core/UrlParams';
Expand Down
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/core/TestLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Promise from '@ephox/wrap-promise-polyfill';
import { ErrorCatcher } from '../errors/ErrorCatcher';

export const load = (scriptUrl: string): Promise<void> =>
Expand Down Expand Up @@ -38,4 +37,4 @@ export const load = (scriptUrl: string): Promise<void> =>

// Add the script to the dom to load it
document.body.appendChild(script);
});
});
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/core/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Suite, Test } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import sourceMappedStackTrace from 'sourcemapped-stacktrace';

// eslint-disable-next-line @typescript-eslint/no-empty-function
Expand Down Expand Up @@ -62,4 +61,4 @@ export const setStack = (error: Error, stack: string | undefined): void => {
} catch (err) {
// Do nothing
}
};
};
9 changes: 5 additions & 4 deletions modules/runner/src/main/ts/reporter/Callbacks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorData, Global } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';

import { HarnessResponse } from '../core/ServerTypes';

export interface TestErrorData {
Expand All @@ -11,7 +11,7 @@ export interface Callbacks {
readonly loadHarness: () => Promise<HarnessResponse>
readonly sendKeepAlive: (session: string) => Promise<void>;
readonly sendInit: (session: string) => Promise<void>;
readonly sendTestStart: (session: string, totalTests: number, file: string, name: string) => Promise<void>;
readonly sendTestStart: (session: string, number: number, totalTests: number, file: string, name: string) => Promise<void>;
readonly sendTestResult: (session: string, file: string, name: string, passed: boolean, time: string, error: TestErrorData | null, skipped: string | null) => Promise<void>;
readonly sendDone: (session: string, error?: string) => Promise<void>;
}
Expand Down Expand Up @@ -64,8 +64,9 @@ export const Callbacks = (): Callbacks => {
});
};

const sendTestStart = (session: string, totalTests: number, file: string, name: string): Promise<void> => {
const sendTestStart = (session: string, number: number, totalTests: number, file: string, name: string): Promise<void> => {
return sendJson('/tests/start', {
number,
totalTests,
session,
file,
Expand Down Expand Up @@ -104,4 +105,4 @@ export const Callbacks = (): Callbacks => {
sendTestResult,
sendDone
};
};
};
50 changes: 42 additions & 8 deletions modules/runner/src/main/ts/reporter/Reporter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { LoggedError, Reporter as ErrorReporter } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { Callbacks } from './Callbacks';
import { UrlParams } from '../core/UrlParams';
import { formatElapsedTime, mapStackTrace, setStack } from '../core/Utils';
Expand Down Expand Up @@ -47,11 +46,15 @@ const mapError = (e: LoggedError) => mapStackTrace(e.stack).then((mappedStack) =

export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi): Reporter => {
const initial = new Date();
let currentCount = params.offset || 0;
const initialOffset = params.offset || 0;
let currentCount = initialOffset;
let passCount = 0;
let skipCount = 0;
let failCount = 0;

// A global list of reports that were sent to the server, we must wait for these before sending `/done` or it may confuse the HUD
const reportsInFlight: Promise<void>[] = [];

const summary = () => ({
offset: Math.max(0, currentCount - 1),
passed: passCount + (params.offset - params.failed - params.skipped),
Expand All @@ -65,6 +68,10 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
let started = false;
const testUi = ui.test();

// In order to prevent overloading the browser's parallel connection count, we only send start notifications when necessary.
// And when we do, we want any subsequent report to be blocked until the start notification has completed.
let startNotification: () => Promise<void>;

const start = (): Promise<void> => {
if (started) {
return Promise.resolve();
Expand All @@ -74,7 +81,17 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
currentCount++;

testUi.start(file, name);
return callbacks.sendTestStart(params.session, totalNumTests, file, name);
startNotification = () => callbacks.sendTestStart(params.session, currentCount, totalNumTests, file, name);

// once at test start and again every 50 tests (a number chosen without any particular reason)
if (currentCount === initialOffset + 1 || currentCount % 50 === 0) {
// run immediately and cache the result for use later
const callback = startNotification();
reportsInFlight.push(callback);
startNotification = () => callback;
}
// don't block, ever ever ever
return Promise.resolve();
}
};

Expand All @@ -92,7 +109,9 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
const testTime = elapsed(starttime);

testUi.pass(testTime, currentCount);
return callbacks.sendTestResult(params.session, file, name, true, testTime, null, null);

// don't block, ever ever ever
return Promise.resolve();
}
};

Expand All @@ -105,7 +124,14 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
const testTime = elapsed(starttime);

testUi.skip(testTime, currentCount);
return callbacks.sendTestResult(params.session, file, name, false, testTime, null, reason);

const report = startNotification().then(() =>
callbacks.sendTestResult(params.session, file, name, false, testTime, null, reason)
);
reportsInFlight.push(report);

// don't block, ever ever ever
return Promise.resolve();
}
};

Expand All @@ -125,7 +151,11 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
};

testUi.fail(err, testTime, currentCount);
return callbacks.sendTestResult(params.session, file, name, false, testTime, error, null);
// make sure we have sent a `start` before we report a failure, otherwise the HUD goes all weird
// this can block, because failure data is critical for the console
return startNotification().then(() =>
callbacks.sendTestResult(params.session, file, name, false, testTime, error, null)
);
});
}
};
Expand All @@ -146,12 +176,16 @@ export const Reporter = (params: UrlParams, callbacks: Callbacks, ui: ReporterUi
};

const textError = error !== undefined ? ErrorReporter.text(error) : undefined;
callbacks.sendDone(params.session, textError).then(setAsDone, setAsDone);

// make sure any in progress updates are sent before we clean up
Promise.all(reportsInFlight).then(() =>
callbacks.sendDone(params.session, textError).then(setAsDone, setAsDone)
);
};

return {
summary,
test,
done
};
};
};
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/runner/Hooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Hook, HookType, RunnableState, Suite, Test } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import * as Context from '../core/Context';
import { SkipError } from '../errors/Errors';
import { runWithErrorCatcher, runWithTimeout } from './Run';
Expand Down Expand Up @@ -75,4 +74,4 @@ export const runAfterEach = (currentTest: Test): Promise<void> => {
} else {
return Promise.resolve();
}
};
};
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/runner/Run.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Context, ExecuteFn, Failure, Runnable, RunnableState, TestThrowable } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { isInternalError, MultipleDone, SkipError } from '../errors/Errors';
import { ErrorCatcher } from '../errors/ErrorCatcher';
import { Timer } from './Timer';
Expand Down Expand Up @@ -128,4 +127,4 @@ export const runWithTimeout = (runnable: Runnable, context: Context, defaultTime
}).then(resolveIfNotTimedOut, reject);
});
}
};
};
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/runner/Runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Suite } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { HarnessResponse } from '../core/ServerTypes';
import { UrlParams } from '../core/UrlParams';
import { noop } from '../core/Utils';
Expand Down Expand Up @@ -116,4 +115,4 @@ export const Runner = (rootSuite: Suite, params: UrlParams, callbacks: Callbacks
init,
run
};
};
};
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/runner/TestRun.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Failure, LoggedError, RunnableState, Suite, Test } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import * as Context from '../core/Context';
import { InternalError, isInternalError, SkipError } from '../errors/Errors';
import { Reporter, TestReporter } from '../reporter/Reporter';
Expand Down Expand Up @@ -111,4 +110,4 @@ export const runSuite = (suite: Suite, state: RunState, actions: RunActions, rep

export const runSuites = (suites: Suite[], state: RunState, actions: RunActions, reporter: RunReporter): Promise<void> => {
return loop(suites, (suite) => runSuite(suite, state, actions, reporter));
};
};
3 changes: 1 addition & 2 deletions modules/runner/src/main/ts/runner/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Suite, Test } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';

export const countTests = (suite: Suite): number =>
suite.tests.length + suite.suites.reduce((acc, suite) => acc + countTests(suite), 0);
Expand Down Expand Up @@ -46,4 +45,4 @@ export const filterOnly = (suite: Suite): void => {
suite.suites.forEach(filterOnly);
suite.suites = onlySuites;
}
};
};
52 changes: 14 additions & 38 deletions modules/runner/src/test/ts/reporter/ReporterTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Failure, LoggedError } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { assert } from 'chai';
import * as fc from 'fast-check';
import { beforeEach, describe, it } from 'mocha';
Expand All @@ -10,6 +9,7 @@ import { noop } from '../TestUtils';

interface StartTestData {
readonly session: string;
readonly currentCount: number;
readonly totalTests: number;
readonly file: string;
readonly name: string;
Expand Down Expand Up @@ -51,8 +51,8 @@ describe('Reporter.test', () => {
loadHarness: () => Promise.resolve({ retries: 0, chunk: 100, stopOnFailure: true, mode: 'manual', timeout: 10000 }),
sendKeepAlive: () => Promise.resolve(),
sendInit: () => Promise.resolve(),
sendTestStart: (session, totalTests, file, name) => {
startTestData.push({ session, totalTests, file, name });
sendTestStart: (session, currentCount, totalTests, file, name) => {
startTestData.push({ session, currentCount, totalTests, file, name });
return Promise.resolve();
},
sendTestResult: (session, file, name, passed, time, error, skipped) => {
Expand Down Expand Up @@ -82,15 +82,16 @@ describe('Reporter.test', () => {
reset();
const test = reporter.test(fileName + 'Test.ts', testName, testCount);
return test.start().then(() => {
assert.equal(startTestData.length, 1);
assert.equal(startTestData.length, 1, 'Checking start test data length');
assert.deepEqual(startTestData[0], {
currentCount: offset + 1,
session: sessionId,
totalTests: testCount,
file: fileName + 'Test.ts',
name: testName
});
}, 'Checking start test data contents');

assert.equal(endTestData.length, 0);
assert.equal(endTestData.length, 0, 'Checking end test data length');
assert.deepEqual(reporter.summary(), {
offset,
passed: offset,
Expand All @@ -103,35 +104,6 @@ describe('Reporter.test', () => {
}));
});

it('should report the session id, file, name, passed state and time on a test success', () => {
return fc.assert(fc.asyncProperty(fc.hexaString(), fc.asciiString(), fc.integer(offset), (fileName, testName, testCount) => {
reset();
const test = reporter.test(fileName + 'Test.ts', testName, testCount);
return test.start()
.then(test.pass)
.then(() => {
assert.equal(endTestData.length, 1);
const data = endTestData[0];
assert.equal(data.session, sessionId);
assert.equal(data.file, fileName + 'Test.ts');
assert.equal(data.name, testName);
assert.isTrue(data.passed);
assert.isNull(data.skipped);
assert.isNull(data.error);
assert.isString(data.time);

assert.deepEqual(reporter.summary(), {
offset,
passed: offset + 1,
failed: 0,
skipped: 0
}, 'Summary has one passed test');

assert.isFalse(doneCalled);
});
}));
});

it('should report the session id, file, name, passed state and time on a skipped test', () => {
return fc.assert(fc.asyncProperty(fc.hexaString(), fc.asciiString(), fc.asciiString(), fc.integer(offset), (fileName, testName, skippedMessage, testCount) => {
reset();
Expand Down Expand Up @@ -192,15 +164,19 @@ describe('Reporter.test', () => {
}));
});

it('should report done', () => {
it('should report done', async () => {
reporter.done();
// let the async callbacks run
await Promise.resolve();
assert.isTrue(doneCalled);
assert.isUndefined(doneError);
});

it('should report done with an error', () => {
it('should report done with an error', async () => {
reporter.done(Failure.prepFailure('Unexpected error occurred'));
// let the async callbacks run
await Promise.resolve();
assert.isTrue(doneCalled);
assert.include(doneError, 'Unexpected error occurred');
});
});
});
3 changes: 1 addition & 2 deletions modules/runner/src/test/ts/runner/RunnerTestUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Hook, HookType, LoggedError, Suite } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { createHook } from '../../../main/ts/core/Hook';
import { Reporter, TestReporter } from '../../../main/ts/reporter/Reporter';
import { RunState } from '../../../main/ts/runner/TestRun';
Expand Down Expand Up @@ -63,4 +62,4 @@ export const createRunState = (offset: number, chunk: number, count = 0): RunSta
chunk,
timeout: TEST_TIMEOUT,
testCount: count
});
});
3 changes: 1 addition & 2 deletions modules/runner/src/test/ts/runner/TestRunTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Context, HookType, RunnableState, Suite, Test } from '@ephox/bedrock-common';
import Promise from '@ephox/wrap-promise-polyfill';
import { assert } from 'chai';
import * as fc from 'fast-check';
import { beforeEach, describe, it } from 'mocha';
Expand Down Expand Up @@ -355,4 +354,4 @@ describe('TestRun.runSuite', () => {
assert.deepEqual(hooks, [ HookType.Before, HookType.BeforeEach, HookType.AfterEach, HookType.BeforeEach, HookType.AfterEach, HookType.After ]);
});
});
});
});
Loading
Loading