mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-06 02:40:15 +00:00
* E2E: run the shared channels tests on CI instead of skipping them CI servers now boot with Shared Channels running (matterwick#106), so the suite runs there and fails setup if the service is missing. * E2E: run demo plugin v0.12.2 in DialogOnlyMode Moves the demo plugin to v0.12.2 and writes DialogOnlyMode: true into its settings entry before it is installed on server B, so it no longer creates a demo user and channels or posts in every team on every server event. Only /dialog is kept, which is all the dialog specs use. The entry still keeps an older build on a reused server out of config saves. * E2E: give a neutral reason when Shared Channels is unavailable hasSharedChannels is false both when the license lacks Shared Channels and when GET /remotecluster fails; point to the globalSetup log line that names the actual reason. * E2E: run the read-only dialog specs on the pool servers With demo plugin v0.12.2 in DialogOnlyMode it no longer posts in every team, so it is installed on every full-suite server (pool A and mutation B) instead of B only. interactive_dialog_plugin and interactive_dialog_collapsible change no server-wide state, so they leave the mutation manifest and run on the pool. interactive_dialog_mobile_upload_disabled stays on B. The shared SITE_3 and smoke servers still get no demo plugin, and CMT still rejects a site 2 that is also a pool site 1. * E2E: remove unused demo plugin helpers apiUploadAndEnablePlugin and apiClearDemoPluginFileUploadKV had no callers (the KV route does not exist). Provisioning installs the plugin, so the install plan they used goes too. * fix(e2e): say why a spec has no result (#10280) * chore: Update NOTICE.txt file with updated dependencies (#10277) Co-authored-by: unified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com> * fix(e2e): say whether a spec with no result had a silent shard or a stopped one A spec that produced no result now says which: no spec of its shard reported (the shard ran or uploaded nothing), or other specs of its shard reported (its Jest process stopped before reaching this spec). E2E triage re-runs the first and judges the second by the spec's own history. The shard of each spec comes from the generate-specs matrix rows; without them the old wording stays. --------- Co-authored-by: unified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com> --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: unified-ci-app[bot] <121569378+unified-ci-app[bot]@users.noreply.github.com>
233 lines
10 KiB
JavaScript
233 lines
10 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
// CI util unit tests: run with `node --test detox/utils/merge-jest-results-for-tsio.test.js`.
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const {describe, it} = require('node:test');
|
|
|
|
const {
|
|
mergeJestResultsForTsio,
|
|
toTsioDetoxSuite,
|
|
relativizeDetoxPath,
|
|
writeMergedJestResultsForTsio,
|
|
appendUnreportedSpecs,
|
|
parseExpectedSpecs,
|
|
parseShards,
|
|
} = require('./merge-jest-results-for-tsio');
|
|
|
|
describe('merge-jest-results-for-tsio', () => {
|
|
it('should keep already-relative paths (any e2e layout) unchanged', () => {
|
|
assert.equal(
|
|
relativizeDetoxPath('detox/e2e/test/foo.e2e.ts'),
|
|
'detox/e2e/test/foo.e2e.ts',
|
|
);
|
|
assert.equal(
|
|
relativizeDetoxPath('e2e/detox/test/foo.e2e.ts'),
|
|
'e2e/detox/test/foo.e2e.ts',
|
|
);
|
|
assert.equal(
|
|
relativizeDetoxPath('e2e/maestro/flows/calls/mute.yml'),
|
|
'e2e/maestro/flows/calls/mute.yml',
|
|
);
|
|
});
|
|
|
|
it('should strip absolute paths against an explicit repo root', () => {
|
|
assert.equal(
|
|
relativizeDetoxPath(
|
|
'/home/runner/work/mattermost-mobile/mattermost-mobile/e2e/detox/test/foo.e2e.ts',
|
|
{repoRoot: '/home/runner/work/mattermost-mobile/mattermost-mobile'},
|
|
),
|
|
'e2e/detox/test/foo.e2e.ts',
|
|
);
|
|
assert.equal(
|
|
relativizeDetoxPath(
|
|
'/home/runner/work/mattermost-mobile/mattermost-mobile/detox/e2e/test/foo.e2e.ts',
|
|
{repoRoot: '/home/runner/work/mattermost-mobile/mattermost-mobile'},
|
|
),
|
|
'detox/e2e/test/foo.e2e.ts',
|
|
);
|
|
});
|
|
|
|
it('should fall back to GitHub Actions /work/<repo>/<repo>/ stripping', () => {
|
|
assert.equal(
|
|
relativizeDetoxPath(
|
|
'/home/runner/work/mattermost-mobile/mattermost-mobile/e2e/detox/test/foo.e2e.ts',
|
|
{repoRoot: '/tmp/not-the-root'},
|
|
),
|
|
'e2e/detox/test/foo.e2e.ts',
|
|
);
|
|
});
|
|
|
|
it('should convert Jest CLI suites (name + assertionResults) to TSIO Detox shape', () => {
|
|
const converted = toTsioDetoxSuite({
|
|
name: '/repo/e2e/detox/test/products/agents/agent_mention.e2e.ts',
|
|
startTime: 1000,
|
|
assertionResults: [{
|
|
ancestorTitles: ['Autocomplete - Agent Mention'],
|
|
duration: 12,
|
|
failureMessages: ['boom'],
|
|
fullName: 'Autocomplete - Agent Mention should show bot',
|
|
status: 'failed',
|
|
title: 'should show bot',
|
|
}],
|
|
}, {repoRoot: '/repo'});
|
|
|
|
assert.deepEqual(converted, {
|
|
testFilePath: 'e2e/detox/test/products/agents/agent_mention.e2e.ts',
|
|
perfStats: {start: 1000},
|
|
testResults: [{
|
|
ancestorTitles: ['Autocomplete - Agent Mention'],
|
|
duration: 12,
|
|
failureMessages: ['boom'],
|
|
fullName: 'Autocomplete - Agent Mention should show bot',
|
|
status: 'failed',
|
|
title: 'should show bot',
|
|
}],
|
|
});
|
|
});
|
|
|
|
it('should keep a retried test\'s earlier failures for TSIO, and nothing for an untried one', () => {
|
|
const converted = toTsioDetoxSuite({
|
|
name: '/repo/detox/e2e/test/threads/reply_to_thread.e2e.ts',
|
|
assertionResults: [
|
|
{fullName: 'Threads MM-T4809_1', title: 'MM-T4809_1', status: 'passed', failureMessages: [], retryReasons: ['Test Failed: No elements found']},
|
|
{fullName: 'Threads MM-T4809_2', title: 'MM-T4809_2', status: 'passed', failureMessages: [], retryReasons: []},
|
|
],
|
|
}, {repoRoot: '/repo'});
|
|
assert.deepEqual(converted.testResults[0].retryReasons, ['Test Failed: No elements found']);
|
|
assert.equal('retryReasons' in converted.testResults[1], false);
|
|
});
|
|
|
|
it('should pass through suites already in TSIO Detox shape', () => {
|
|
const converted = toTsioDetoxSuite({
|
|
testFilePath: 'e2e/detox/test/foo.e2e.ts',
|
|
testResults: [{
|
|
ancestorTitles: [],
|
|
duration: 1,
|
|
failureMessages: [],
|
|
fullName: 'foo',
|
|
status: 'passed',
|
|
title: 'foo',
|
|
}],
|
|
});
|
|
|
|
assert.equal(converted.testFilePath, 'e2e/detox/test/foo.e2e.ts');
|
|
assert.equal(converted.testResults.length, 1);
|
|
assert.equal(converted.testResults[0].status, 'passed');
|
|
});
|
|
|
|
it('should merge multiple CLI shard files into one TSIO payload with real test cases', () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tsio-merge-'));
|
|
const a = path.join(dir, 'a.json');
|
|
const b = path.join(dir, 'b.json');
|
|
fs.writeFileSync(a, JSON.stringify({
|
|
numTotalTests: 1,
|
|
testResults: [{
|
|
name: '/x/e2e/detox/test/a.e2e.ts',
|
|
assertionResults: [{
|
|
ancestorTitles: ['A'],
|
|
duration: 1,
|
|
failureMessages: [],
|
|
fullName: 'A pass',
|
|
status: 'passed',
|
|
title: 'pass',
|
|
}],
|
|
}],
|
|
}));
|
|
fs.writeFileSync(b, JSON.stringify({
|
|
numTotalTests: 1,
|
|
testResults: [{
|
|
name: '/x/e2e/detox/test/b.e2e.ts',
|
|
assertionResults: [{
|
|
ancestorTitles: ['B'],
|
|
duration: 2,
|
|
failureMessages: ['fail'],
|
|
fullName: 'B fail',
|
|
status: 'failed',
|
|
title: 'fail',
|
|
}],
|
|
}],
|
|
}));
|
|
|
|
const opts = {repoRoot: '/x'};
|
|
const merged = mergeJestResultsForTsio([a, b], opts);
|
|
assert.equal(merged.testResults.length, 2);
|
|
assert.equal(merged.testResults[0].testFilePath, 'e2e/detox/test/a.e2e.ts');
|
|
assert.equal(merged.testResults[0].testResults[0].status, 'passed');
|
|
assert.equal(merged.testResults[1].testFilePath, 'e2e/detox/test/b.e2e.ts');
|
|
assert.equal(merged.testResults[1].testResults[0].status, 'failed');
|
|
|
|
assert.equal(merged.testResults[0].assertionResults, undefined);
|
|
assert.equal(merged.testResults[0].name, undefined);
|
|
|
|
const out = path.join(dir, 'merged.json');
|
|
const stats = writeMergedJestResultsForTsio([a, b], out, opts);
|
|
assert.deepEqual(stats, {suites: 2, tests: 2});
|
|
assert.equal(JSON.parse(fs.readFileSync(out, 'utf8')).testResults.length, 2);
|
|
|
|
fs.rmSync(dir, {recursive: true, force: true});
|
|
});
|
|
|
|
it('should mark specs that never reported as failed', () => {
|
|
// main d943628: machine-4's Jest was killed mid-run, jest-stare's partial report
|
|
// was promoted, and 3 specs vanished from a report that published 100% green.
|
|
const merged = {testResults: [{testFilePath: 'detox/e2e/test/a.e2e.ts', testResults: [{status: 'passed'}]}]};
|
|
const missing = appendUnreportedSpecs(merged, [
|
|
'detox/e2e/test/a.e2e.ts',
|
|
'detox/e2e/test/b.e2e.ts',
|
|
'detox/e2e/test/c.e2e.ts',
|
|
]);
|
|
|
|
assert.deepEqual(missing, ['detox/e2e/test/b.e2e.ts', 'detox/e2e/test/c.e2e.ts']);
|
|
assert.equal(merged.testResults.length, 3, 'every assigned spec must appear');
|
|
assert.equal(merged.testResults[1].testResults[0].status, 'failed');
|
|
assert.match(merged.testResults[1].testResults[0].failureMessages[0], /produced no result/);
|
|
});
|
|
|
|
it('should say whether the spec\'s shard reported nothing or stopped before reaching it', () => {
|
|
// Triage re-runs a shard that reported nothing, and judges a spec whose shard stopped before it by its history.
|
|
const merged = {testResults: [{testFilePath: 'detox/e2e/test/a.e2e.ts', testResults: [{status: 'passed'}]}]};
|
|
const shards = [['detox/e2e/test/a.e2e.ts', 'detox/e2e/test/b.e2e.ts'], ['detox/e2e/test/c.e2e.ts', 'detox/e2e/test/d.e2e.ts']];
|
|
appendUnreportedSpecs(merged, shards.flat(), shards);
|
|
|
|
const reason = (spec) => merged.testResults.find((s) => s.testFilePath === spec).testResults[0].failureMessages[0];
|
|
assert.equal(reason('detox/e2e/test/b.e2e.ts'), 'detox/e2e/test/b.e2e.ts was assigned to a shard but produced no result: other specs of its shard reported, so its Jest process stopped before reaching this spec.');
|
|
assert.equal(reason('detox/e2e/test/c.e2e.ts'), 'detox/e2e/test/c.e2e.ts was assigned to a shard but produced no result: no spec of its shard reported, so the shard ran or uploaded nothing.');
|
|
assert.match(reason('detox/e2e/test/d.e2e.ts'), /no spec of its shard reported/);
|
|
});
|
|
|
|
it('should read each generate-specs row as one shard', () => {
|
|
assert.deepEqual(parseShards(JSON.stringify({include: [{specs: 'a.e2e.ts b.e2e.ts'}, {specs: 'c.e2e.ts'}, {}]})), [['a.e2e.ts', 'b.e2e.ts'], ['c.e2e.ts']]);
|
|
assert.deepEqual(parseShards(''), []);
|
|
});
|
|
|
|
it('should not touch a complete report', () => {
|
|
const merged = {testResults: [{testFilePath: 'detox/e2e/test/a.e2e.ts'}]};
|
|
assert.deepEqual(appendUnreportedSpecs(merged, ['detox/e2e/test/a.e2e.ts']), []);
|
|
assert.equal(merged.testResults.length, 1);
|
|
});
|
|
|
|
it('should mark every spec failed when no shard reported at all', () => {
|
|
// If the merge exits before this, TSIO never receives a report and the commit
|
|
// status stays pending instead of failing.
|
|
const merged = mergeJestResultsForTsio([]);
|
|
const missing = appendUnreportedSpecs(merged, ['detox/e2e/test/a.e2e.ts', 'detox/e2e/test/b.e2e.ts']);
|
|
assert.equal(missing.length, 2);
|
|
assert.equal(merged.testResults.length, 2, 'a run with no shard reports must still emit a report');
|
|
});
|
|
|
|
it('should flatten a generate-specs matrix and de-duplicate', () => {
|
|
const specs = parseExpectedSpecs(JSON.stringify({include: [
|
|
{runId: 1, specs: 'a.e2e.ts b.e2e.ts'},
|
|
{runId: 2, specs: 'b.e2e.ts c.e2e.ts'},
|
|
]}));
|
|
assert.deepEqual(specs, ['a.e2e.ts', 'b.e2e.ts', 'c.e2e.ts']);
|
|
assert.deepEqual(parseExpectedSpecs(''), []);
|
|
assert.deepEqual(parseExpectedSpecs('{}'), []);
|
|
});
|
|
});
|