Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 21 additions & 1 deletion bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,20 @@ exports.setNodeVersion = (bsConfig, args) => {
}

// specs can be passed from bstack configuration file
// True when the spec list for this run comes from BROWSERSTACK_RERUN_TESTS rather than
// the user's config/CLI args. Shared by setUserSpecs and getNumberOfSpecFiles so the two
// cannot drift apart — getNumberOfSpecFiles rewrites specs only for runs setUserSpecs
// actually sourced from the re-run env.
exports.isReRunSpecsSession = () => {
return o11yHelpers.isBrowserstackInfra()
&& o11yHelpers.isTestObservabilitySession()
&& o11yHelpers.shouldReRunObservabilityTests();
}

// specs can be passed via command line args as a string
// command line args takes precedence over config
exports.setUserSpecs = (bsConfig, args) => {
if(o11yHelpers.isBrowserstackInfra() && o11yHelpers.isTestObservabilitySession() && o11yHelpers.shouldReRunObservabilityTests()) {
if(this.isReRunSpecsSession()) {
// BROWSERSTACK_RERUN_TESTS arrives comma+space separated (e.g. "a.ts, b.ts"); normalise
// like the other spec sources below, else sanitizeSpecsPattern builds "{a.ts, b.ts}" whose
// space-prefixed brace alternatives never match and the failed-spec filter collapses.
Expand Down Expand Up @@ -1232,6 +1242,16 @@ exports.getNumberOfSpecFiles = (bsConfig, args, cypressConfig, turboScaleSession
files = files.map((x) => { return x.replaceAll("\\", "/") })
// setting specs for turboScale as we don't have patched API for turboscale so we will rely on info from CLI
bsConfig.run_settings.specs = files;
} else if (this.isReRunSpecsSession() && files.length) {
// BROWSERSTACK_RERUN_TESTS arrives as bare filenames ("a.ts,b.ts"). run_settings is
// forwarded to the remote machines verbatim, and a bare filename does not resolve
// against the project root there, so Cypress exits having run no spec at all. The
// glob above already resolved them against cypressProjectDir — persist those
// project-relative paths so the remote receives something it can resolve.
bsConfig.run_settings.specs = files
.map((file) => path.relative(bsConfig.run_settings.cypressProjectDir, file).replaceAll("\\", "/"))
.join(",");
logger.debug(`Re-run specs resolved to project-relative paths: ${bsConfig.run_settings.specs}`);
}
return files;
} catch (err) {
Expand Down
70 changes: 70 additions & 0 deletions test/unit/bin/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,76 @@ describe('utils', () => {
});

describe('getNumberOfSpecFiles', () => {
context('when re-running observability failed tests (SDK-7124)', () => {
let rerunStubs = [];
const specPattern = `cypress/e2e/**/*.+(${constant.specFileTypes.join('|')})`;

const stubGlob = () => {
const globStub = sinon.stub(glob, 'sync');
globStub.withArgs(specPattern).returns([
'cypress/e2e/FlightOffer/FO-E2E-02.ts',
'cypress/e2e/FlightOffer/FO-E2E-05.ts',
'cypress/e2e/FlightOffer/FO-E2E-09.ts',
]);
globStub.withArgs('{FO-E2E-02.ts,FO-E2E-05.ts}').returns([
'cypress/e2e/FlightOffer/FO-E2E-02.ts',
'cypress/e2e/FlightOffer/FO-E2E-05.ts',
]);
return globStub;
};

const buildConfig = (specs) => ({
run_settings: {
cypressTestSuiteType: CYPRESS_V10_AND_ABOVE_TYPE,
specs: specs,
cypressProjectDir: '/repo',
},
});

beforeEach(() => {
rerunStubs.push(sinon.stub(o11yHelpers, 'isBrowserstackInfra').returns(true));
rerunStubs.push(sinon.stub(o11yHelpers, 'isTestObservabilitySession').returns(true));
rerunStubs.push(sinon.stub(o11yHelpers, 'shouldReRunObservabilityTests').returns(true));
});

afterEach(() => {
rerunStubs.forEach((s) => s.restore());
rerunStubs = [];
if (glob.sync.restore) glob.sync.restore();
});

it('rewrites the bare rerun filenames to project-relative paths for the remote machines', () => {
stubGlob();
// shape setUserSpecs leaves behind for a rerun: bare filenames, comma separated
let bsConfig = buildConfig('FO-E2E-02.ts,FO-E2E-05.ts');

const result = utils.getNumberOfSpecFiles(bsConfig, {}, {});

expect(result.length).to.eql(2);
// run_settings is forwarded to the remote verbatim, so it must carry resolvable paths
expect(bsConfig.run_settings.specs).to.be.eq(
'cypress/e2e/FlightOffer/FO-E2E-02.ts,cypress/e2e/FlightOffer/FO-E2E-05.ts'
);
});

it('leaves specs untouched when this is not a rerun session', () => {
rerunStubs.forEach((s) => s.restore());
rerunStubs = [];
sinon.stub(o11yHelpers, 'isBrowserstackInfra').returns(false);
sinon.stub(o11yHelpers, 'isTestObservabilitySession').returns(false);
sinon.stub(o11yHelpers, 'shouldReRunObservabilityTests').returns(false);
rerunStubs.push(o11yHelpers.isBrowserstackInfra);
rerunStubs.push(o11yHelpers.isTestObservabilitySession);
rerunStubs.push(o11yHelpers.shouldReRunObservabilityTests);
stubGlob();
let bsConfig = buildConfig('FO-E2E-02.ts,FO-E2E-05.ts');

utils.getNumberOfSpecFiles(bsConfig, {}, {});

expect(bsConfig.run_settings.specs).to.be.eq('FO-E2E-02.ts,FO-E2E-05.ts');
});
});

it('should return files matching with run_settings.specs and under default folder if cypress v <= 9 and no integration/testFiles patterm provided', () => {
let globStub = sinon.stub(glob, 'sync')
globStub.withArgs('cypress/integration/foo*.js')
Expand Down
Loading