Content-Length: 1643108 | pFad | http://github.com/mablhq/github-run-tests-action/commit/007096312c3d38f89710c3b1e663bd9842d8c26a

95 v1.9.0 release · mablhq/github-run-tests-action@0070963 · GitHub
Skip to content

Commit 0070963

Browse files
committed
v1.9.0 release
1 parent 3b77a69 commit 0070963

File tree

184 files changed

+35289
-109280
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

184 files changed

+35289
-109280
lines changed

lib/src/entities/Application.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

lib/src/entities/Deployment.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

lib/src/entities/ExecutionResult.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

lib/src/index.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
"use strict";
2+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3+
if (k2 === undefined) k2 = k;
4+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5+
}) : (function(o, m, k, k2) {
6+
if (k2 === undefined) k2 = k;
7+
o[k2] = m[k];
8+
}));
9+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10+
Object.defineProperty(o, "default", { enumerable: true, value: v });
11+
}) : function(o, v) {
12+
o["default"] = v;
13+
});
14+
var __importStar = (this && this.__importStar) || function (mod) {
15+
if (mod && mod.__esModule) return mod;
16+
var result = {};
17+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18+
__setModuleDefault(result, mod);
19+
return result;
20+
};
21+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23+
return new (P || (P = Promise))(function (resolve, reject) {
24+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27+
step((generator = generator.apply(thisArg, _arguments || [])).next());
28+
});
29+
};
30+
var __importDefault = (this && this.__importDefault) || function (mod) {
31+
return (mod && mod.__esModule) ? mod : { "default": mod };
32+
};
33+
Object.defineProperty(exports, "__esModule", { value: true });
34+
const axios_1 = __importDefault(require("axios"));
35+
const mablApiClient_1 = require("./mablApiClient");
36+
const table_1 = require("./table");
37+
const core = __importStar(require("@actions/core"));
38+
const github = __importStar(require("@actions/github"));
39+
const DEFAULT_MABL_APP_URL = 'https://app.mabl.com';
40+
const EXECUTION_POLL_INTERVAL_MILLIS = 10000;
41+
const EXECUTION_COMPLETED_STATUSES = [
42+
'succeeded',
43+
'failed',
44+
'cancelled',
45+
'completed',
46+
'terminated',
47+
];
48+
const GITHUB_BASE_URL = 'https://api.github.com';
49+
function run() {
50+
var _a, _b, _c;
51+
return __awaiter(this, void 0, void 0, function* () {
52+
try {
53+
core.startGroup('Gathering inputs');
54+
const applicationId = core.getInput('application-id', {
55+
required: false,
56+
});
57+
const environmentId = core.getInput('environment-id', {
58+
required: false,
59+
});
60+
const apiKey = process.env.MABL_API_KEY || '';
61+
if (!apiKey) {
62+
core.setFailed('MABL_API_KEY required');
63+
}
64+
const browserTypes = core.getInput('browser-types', {
65+
required: false,
66+
});
67+
const uri = core.getInput('uri', { required: false });
68+
const rebaselineImages = parseBoolean(core.getInput('rebaseline-images', {
69+
required: false,
70+
}));
71+
const setStaticBaseline = parseBoolean(core.getInput('set-static-baseline', {
72+
required: false,
73+
}));
74+
const continueOnPlanFailure = parseBoolean(core.getInput('continue-on-failure', { required: false }));
75+
const pullRequest = yield getRelatedPullRequest();
76+
const eventTimeString = core.getInput('event-time', { required: false });
77+
const eventTime = eventTimeString ? parseInt(eventTimeString) : Date.now();
78+
let properties = {
79+
triggering_event_name: process.env.GITHUB_EVENT_NAME,
80+
repository_commit_username: process.env.GITHUB_ACTOR,
81+
repository_action: process.env.GITHUB_ACTION,
82+
repository_branch_name: process.env.GITHUB_REF,
83+
repository_name: process.env.GITHUB_REPOSITORY,
84+
repository_url: `git@github.com:${process.env.GITHUB_REPOSITORY}.git`,
85+
};
86+
if (pullRequest) {
87+
properties = Object.assign(properties, {
88+
repository_pull_request_url: pullRequest.url,
89+
repository_pull_request_number: pullRequest.number,
90+
repository_pull_request_title: pullRequest.title,
91+
repository_pull_request_created_at: pullRequest.created_at,
92+
});
93+
if (pullRequest.merged_at) {
94+
properties.repository_pull_request_merged_at = pullRequest.merged_at;
95+
}
96+
}
97+
const baseApiUrl = (_a = process.env.APP_URL) !== null && _a !== void 0 ? _a : DEFAULT_MABL_APP_URL;
98+
const revision = process.env.GITHUB_EVENT_NAME === 'pull_request'
99+
? (_c = (_b = github.context.payload.pull_request) === null || _b === void 0 ? void 0 : _b.head) === null || _c === void 0 ? void 0 : _c.sha
100+
: process.env.GITHUB_SHA;
101+
core.info(`Using git revision [${revision}]`);
102+
core.endGroup();
103+
core.startGroup('Creating deployment event');
104+
const apiClient = new mablApiClient_1.MablApiClient(apiKey);
105+
const deployment = yield apiClient.postDeploymentEvent(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, eventTime, properties);
106+
core.setOutput('mabl-deployment-id', deployment.id);
107+
let outputLink = baseApiUrl;
108+
if (applicationId) {
109+
const application = yield apiClient.getApplication(applicationId);
110+
outputLink = `${baseApiUrl}/workspaces/${application.organization_id}/events/${deployment.id}`;
111+
core.info(`Deployment triggered. View output at: ${outputLink}`);
112+
}
113+
core.startGroup('Await completion of tests');
114+
let executionComplete = false;
115+
while (!executionComplete) {
116+
yield new Promise((resolve) => setTimeout(resolve, EXECUTION_POLL_INTERVAL_MILLIS));
117+
const executionResult = yield apiClient.getExecutionResults(deployment.id);
118+
if (executionResult === null || executionResult === void 0 ? void 0 : executionResult.executions) {
119+
const pendingExecutions = getExecutionsStillPending(executionResult);
120+
if (pendingExecutions.length === 0) {
121+
executionComplete = true;
122+
}
123+
else {
124+
core.info(`${pendingExecutions.length} mabl plan(s) are still running`);
125+
}
126+
}
127+
}
128+
core.info('mabl deployment runs have completed');
129+
core.endGroup();
130+
core.startGroup('Fetch execution results');
131+
const finalExecutionResult = yield apiClient.getExecutionResults(deployment.id);
132+
finalExecutionResult.executions.forEach((execution) => {
133+
core.info(table_1.prettyFormatExecution(execution));
134+
});
135+
core.setOutput('plans_run', '' + finalExecutionResult.plan_execution_metrics.total);
136+
core.setOutput('plans_passed', '' + finalExecutionResult.plan_execution_metrics.passed);
137+
core.setOutput('plans_failed', '' + finalExecutionResult.plan_execution_metrics.failed);
138+
core.setOutput('tests_run', '' + finalExecutionResult.journey_execution_metrics.total);
139+
core.setOutput('tests_passed', '' + finalExecutionResult.journey_execution_metrics.passed);
140+
core.setOutput('tests_failed', '' + finalExecutionResult.journey_execution_metrics.failed);
141+
if (finalExecutionResult.journey_execution_metrics.failed === 0) {
142+
core.debug('Deployment plans passed');
143+
}
144+
else if (continueOnPlanFailure) {
145+
core.warning(`There were ${finalExecutionResult.journey_execution_metrics.failed} test failures but the continueOnPlanFailure flag is set so the task has been marked as passing`);
146+
}
147+
else {
148+
core.setFailed(`${finalExecutionResult.journey_execution_metrics.failed} mabl test(s) failed`);
149+
}
150+
core.endGroup();
151+
}
152+
catch (err) {
153+
core.setFailed(`mabl deployment task failed for the following reason: ${err}`);
154+
}
155+
});
156+
}
157+
function parseBoolean(toParse) {
158+
return (toParse === null || toParse === void 0 ? void 0 : toParse.toLowerCase()) === 'true';
159+
}
160+
function getExecutionsStillPending(executionResult) {
161+
return executionResult.executions.filter((execution) => {
162+
return !(EXECUTION_COMPLETED_STATUSES.includes(execution.status) &&
163+
execution.stop_time);
164+
});
165+
}
166+
function getRelatedPullRequest() {
167+
var _a;
168+
return __awaiter(this, void 0, void 0, function* () {
169+
const targetUrl = `${GITHUB_BASE_URL}/repos/${process.env.GITHUB_REPOSITORY}/commits/${process.env.GITHUB_SHA}/pulls`;
170+
const githubToken = process.env.GITHUB_TOKEN;
171+
if (!githubToken) {
172+
return;
173+
}
174+
const config = {
175+
headers: {
176+
Authorization: `token ${githubToken}`,
177+
Accept: 'application/vnd.github.groot-preview+json',
178+
'Content-Type': 'application/json',
179+
'User-Agent': 'mabl-action',
180+
},
181+
};
182+
const client = axios_1.default.create(config);
183+
try {
184+
const response = yield client.get(targetUrl, config);
185+
return (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a[0];
186+
}
187+
catch (error) {
188+
if (error.status !== 404) {
189+
core.warning(error.message);
190+
}
191+
}
192+
return;
193+
});
194+
}
195+
run();

lib/src/interfaces.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

lib/src/mablApiClient.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"use strict";
2+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4+
return new (P || (P = Promise))(function (resolve, reject) {
5+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8+
step((generator = generator.apply(thisArg, _arguments || [])).next());
9+
});
10+
};
11+
var __importDefault = (this && this.__importDefault) || function (mod) {
12+
return (mod && mod.__esModule) ? mod : { "default": mod };
13+
};
14+
Object.defineProperty(exports, "__esModule", { value: true });
15+
exports.MablApiClient = void 0;
16+
const async_retry_1 = __importDefault(require("async-retry"));
17+
const axios_1 = __importDefault(require("axios"));
18+
class MablApiClient {
19+
constructor(apiKey) {
20+
var _a;
21+
this.baseUrl = (_a = process.env.APP_URL) !== null && _a !== void 0 ? _a : 'https://api.mabl.com';
22+
const config = {
23+
headers: {
24+
'User-Agent': 'github-run-tests-action',
25+
Accept: 'application/json',
26+
'Content-Type': 'application/json',
27+
},
28+
auth: {
29+
username: 'key',
30+
password: apiKey,
31+
},
32+
};
33+
this.httpClient = axios_1.default.create(config);
34+
}
35+
makeGetRequest(url) {
36+
return __awaiter(this, void 0, void 0, function* () {
37+
return async_retry_1.default(() => __awaiter(this, void 0, void 0, function* () {
38+
var _a;
39+
const response = yield this.httpClient.get(url);
40+
if (((_a = response.status) !== null && _a !== void 0 ? _a : 400) >= 400) {
41+
throw new Error(`[${response.status} - ${response.statusText}]`);
42+
}
43+
return response.data;
44+
}), {
45+
retries: 3,
46+
});
47+
});
48+
}
49+
makePostRequest(url, requestBody) {
50+
return __awaiter(this, void 0, void 0, function* () {
51+
return async_retry_1.default(() => __awaiter(this, void 0, void 0, function* () {
52+
var _a;
53+
const response = yield this.httpClient.post(url, JSON.stringify(requestBody));
54+
if (((_a = response.status) !== null && _a !== void 0 ? _a : 400) >= 400) {
55+
throw new Error(`[${response.status} - ${response.statusText}]`);
56+
}
57+
return response.data;
58+
}), {
59+
retries: 3,
60+
});
61+
});
62+
}
63+
getApplication(applicationId) {
64+
return __awaiter(this, void 0, void 0, function* () {
65+
try {
66+
return yield this.makeGetRequest(`${this.baseUrl}/v1/applications/${applicationId}`);
67+
}
68+
catch (error) {
69+
throw new Error(`failed to get mabl application ($applicationId) from the API ${error}`);
70+
}
71+
});
72+
}
73+
getExecutionResults(eventId) {
74+
return __awaiter(this, void 0, void 0, function* () {
75+
try {
76+
return yield this.makeGetRequest(`${this.baseUrl}/execution/result/event/${eventId}`);
77+
}
78+
catch (error) {
79+
throw new Error(`failed to get mabl execution results for event ${eventId} from the API ${error}`);
80+
}
81+
});
82+
}
83+
postDeploymentEvent(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, revision, eventTime, properties) {
84+
return __awaiter(this, void 0, void 0, function* () {
85+
try {
86+
const requestBody = this.buildRequestBody(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, eventTime, properties, revision);
87+
return yield this.makePostRequest(`${this.baseUrl}/events/deployment/`, requestBody);
88+
}
89+
catch (e) {
90+
throw new Error(`failed to create deployment through mabl API ${e}`);
91+
}
92+
});
93+
}
94+
buildRequestBody(applicationId, environmentId, browserTypes, uri, rebaselineImages, setStaticBaseline, event_time, properties, revision) {
95+
const requestBody = {};
96+
if (environmentId) {
97+
requestBody.environment_id = environmentId;
98+
}
99+
if (applicationId) {
100+
requestBody.application_id = applicationId;
101+
}
102+
const planOverrides = {};
103+
if (browserTypes) {
104+
planOverrides.browser_types = browserTypes.split(',');
105+
}
106+
if (uri) {
107+
planOverrides.uri = uri;
108+
}
109+
requestBody.plan_overrides = planOverrides;
110+
if (revision) {
111+
requestBody.revision = revision;
112+
}
113+
if (event_time) {
114+
requestBody.event_time = event_time;
115+
}
116+
if (properties) {
117+
requestBody.properties = properties;
118+
}
119+
const actions = {};
120+
if (rebaselineImages) {
121+
actions.rebaseline_images = rebaselineImages;
122+
}
123+
if (setStaticBaseline) {
124+
actions.set_static_baseline = setStaticBaseline;
125+
}
126+
requestBody.actions = actions;
127+
return requestBody;
128+
}
129+
}
130+
exports.MablApiClient = MablApiClient;

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/mablhq/github-run-tests-action/commit/007096312c3d38f89710c3b1e663bd9842d8c26a

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy