Skip to content

Commit

Permalink
add function to call creating job API from test-composer (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
tianfeng92 authored Dec 17, 2020
1 parent 6cd3a32 commit 0e0ebb7
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 17 deletions.
8 changes: 5 additions & 3 deletions src/cypress-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const cypress = require('cypress');
const yargs = require('yargs/yargs');
const _ = require('lodash');

const report = async (results, browserName, runCfg, suiteName) => {
const report = async (results, browserName, runCfg, suiteName, startTime, endTime) => {
// Prepare the assets
const runs = results.runs || [];
let specFiles = runs.map((run) => run.spec.name);
Expand All @@ -25,7 +25,7 @@ const report = async (results, browserName, runCfg, suiteName) => {
runCfg.resultsDir,
);

await sauceReporter(runCfg, suiteName, browserName, assets, failures);
await sauceReporter(runCfg, suiteName, browserName, assets, failures, startTime, endTime);

return failures === 0;
};
Expand Down Expand Up @@ -77,9 +77,11 @@ const cypressRunner = async function (runCfgPath, suiteName) {

await installDependencies(runCfg);
let cypressOpts = getCypressOpts(runCfg, suiteName);
let startTime = new Date().toISOString();
const results = await cypress.run(cypressOpts);
let endTime = new Date().toISOString();

return await report(results, cypressOpts.browser, runCfg, suiteName);
return await report(results, cypressOpts.browser, runCfg, suiteName, startTime, endTime);
};

// For dev and test purposes, this allows us to run our Cypress Runner from command line
Expand Down
38 changes: 36 additions & 2 deletions src/sauce-reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,40 @@ SauceReporter.createJobShell = async (api, testName, tags, browserName) => {
return sessionId || 0;
};


// TODO Tian: this method is a temporary solution for creating jobs via test-composer.
// Once the global data store is ready, this method will be deprecated.
SauceReporter.createJobWorkaround = async (api, testName, metadata, browserName, passed, startTime, endTime) => {
const body = {
name: testName,
user: process.env.SAUCE_USERNAME,
startTime,
endTime,
framework: 'cypress',
frameworkVersion: '*', // collect
status: 'complete',
errors: [],
passed,
tags: metadata.tags,
build: metadata.build,
browserName,
browserVersion: '*',
platformName: '*' // in docker, no specified platform
};

let sessionId;
await api.createJob(
body
).then(
(resp) => {
sessionId = resp.ID;
},
(e) => console.error('Create job failed: ', e.stack)
);

return sessionId || 0;
};

SauceReporter.createJobLegacy = async (api, region, browserName, testName, metadata) => {
try {
await remote({
Expand Down Expand Up @@ -144,7 +178,7 @@ SauceReporter.prepareAssets = async (specFiles, resultsFolder) => {
return assets;
};

SauceReporter.sauceReporter = async (runCfg, suiteName, browserName, assets, failures) => {
SauceReporter.sauceReporter = async (runCfg, suiteName, browserName, assets, failures, startTime, endTime) => {
const { sauce = {} } = runCfg;
const { metadata = {} } = sauce;
const baseTestName = metadata.name || `Test ${+new Date()}`;
Expand All @@ -161,7 +195,7 @@ SauceReporter.sauceReporter = async (runCfg, suiteName, browserName, assets, fai
if (process.env.ENABLE_DATA_STORE) {
sessionId = await SauceReporter.createJobShell(api, testName, metadata.tags, browserName);
} else {
sessionId = await SauceReporter.createJobLegacy(api, region, browserName, testName, metadata);
sessionId = await SauceReporter.createJobWorkaround(api, testName, metadata, browserName, failures === 0, startTime, endTime);
}

if (!sessionId) {
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/src/__snapshots__/cypress-runner.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Array [
"spec-b",
],
Array [],
"Date: 1",
"Date: 2",
],
]
`;
Expand Down
3 changes: 0 additions & 3 deletions tests/unit/src/__snapshots__/sauce-reporter.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ Array [

exports[`SauceReporter .sauceReporter should output err when upload failed 1`] = `
Array [
Array [
[TypeError: Cannot read property 'catch' of undefined],
],
Array [
"some fake error",
],
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/src/cypress-runner.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ describe('.cypressRunner', function () {
loadRunConfig.mockImplementation(() => fakeRunnerJson);
fs.existsSync.mockImplementation(() => true);
fs.readFileSync.mockImplementation(() => JSON.stringify(fakeRunnerJson));

// Mock the dates so that it's deterministic
const isoDateSpy = jest.spyOn(Date.prototype, 'toISOString');
let day = 0;
isoDateSpy.mockImplementation(() => `Date: ${++day}`);
});
it('can call Cypress.run with basic args', async function () {
process.env.SAUCE_USERNAME = 'fake-sauce-username';
Expand Down
33 changes: 24 additions & 9 deletions tests/unit/src/sauce-reporter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ describe('SauceReporter', function () {
}
}
};
const start = new Date().toISOString();
const end = new Date().toISOString();

describe('.prepareAssets', function () {
beforeEach(function () {
fs.existsSync.mockClear();
Expand All @@ -35,13 +38,19 @@ describe('SauceReporter', function () {
});
});
describe('.sauceReporter', function () {
let prepareAssetsSpy, uploadJobAssetsSpy, createJobSpy, backupEnv = process.env;
let prepareAssetsSpy, uploadJobAssetsSpy, createJobSpy, createResultSpy, createJobWorkaroundSpy, backupEnv = process.env;
beforeEach(function () {
createJobWorkaroundSpy = jest.spyOn(SauceReporter, 'createJobWorkaround');
// eslint-disable-next-line require-await
createJobWorkaroundSpy.mockImplementation(async () => 'fake-session-id');
webdriverio.remote.mockImplementation(function () {});
prepareAssetsSpy = jest.spyOn(SauceReporter, 'prepareAssets');
createJobWorkaroundSpy = jest.spyOn(SauceReporter, 'createJobWorkaround');
createJobWorkaroundSpy.mockImplementation(async () => await 'fake-session-id');
// eslint-disable-next-line require-await
uploadJobAssetsSpy = jest.fn().mockImplementation(async () => ({errors: ['some fake error']}));
createJobSpy = jest.fn().mockImplementation(async () => (await {sessionId: '123'}));
createResultSpy = jest.fn().mockImplementation(async () => (await {sessionId: '123'}));
SauceLabs.default.mockImplementation(function () {
// eslint-disable-next-line require-await
this.listJobs = async () => ({
Expand All @@ -50,51 +59,57 @@ describe('SauceReporter', function () {
this.uploadJobAssets = uploadJobAssetsSpy;
this.updateJob = async () => { };
this.createResultJob = createJobSpy;
this.createJob = createResultSpy;
});
process.env.SAUCE_USERNAME = 'fake-user';
});
afterEach(function () {
process.env = backupEnv;
});

it('should call uploadJobAssets on SauceLabs api', async function () {
prepareAssetsSpy.mockReturnValue(['asset/one', 'asset/two']);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0);
// eslint-disable-next-line require-await
prepareAssetsSpy.mockImplementation(async () => ['asset/one', 'asset/two']);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0, start, end);
expect(uploadJobAssetsSpy.mock.calls).toEqual([
['a', {'files': ['asset/one', 'asset/two']}]
['fake-session-id', {'files': ['asset/one', 'asset/two']}]
]);
});
it('should output err when upload failed', async function () {
let consoleErrorSpy = jest.spyOn(global.console, 'error');
prepareAssetsSpy.mockReturnValue(['asset/one', 'asset/two']);
expect(await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0)).toBeUndefined();
//createResultSpy.mockReturnValue({sessionId: 'a'});
expect(await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0, start, end)).toBeUndefined();
expect(uploadJobAssetsSpy.mock.calls).toEqual([
['a', {'files': ['asset/one', 'asset/two']}]
['fake-session-id', {'files': ['asset/one', 'asset/two']}]
]);
expect(consoleErrorSpy.mock.calls).toMatchSnapshot();
});
it('should not push assets when no sessionId from SauceLabs API', async function () {
//createResultSpy.mockReturnValue({});
SauceLabs.default.mockImplementation(function () {
// eslint-disable-next-line require-await
this.listJobs = async () => ({
jobs: []
});
this.uploadJobAssets = uploadJobAssetsSpy;
this.updateJob = async () => {};
this.createJob = createResultSpy;
});

prepareAssetsSpy.mockReturnValue(['asset/one', 'asset/two']);
expect(await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0)).toBeDefined();
expect(await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0, start, end)).toBeUndefined();
});
it ('should create job via global data store', async function () {
process.env.ENABLE_DATA_STORE = 'true';
prepareAssetsSpy.mockReturnValue(['asset/one', 'asset/two']);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0, start, end);
expect(createJobSpy.mock.calls).toMatchSnapshot();
});
it ('should fail when global data store throws error', async function () {
process.env.ENABLE_DATA_STORE = 'true';
prepareAssetsSpy.mockReturnValue(['asset/one', 'asset/two']);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0);
await SauceReporter.sauceReporter(fakeRunConfig, 'build', 'browser', ['asset/one', 'asset/two'], 0, start, end);
expect(createJobSpy).toBeCalled();
});
});
Expand Down

0 comments on commit 0e0ebb7

Please sign in to comment.