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

✨ Implement explicit Android support #29

Merged
merged 9 commits into from
Jul 19, 2023
Merged
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
3 changes: 1 addition & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ name: Trigger Test on TestIO
author: Staffbase GmbH

description: |-
Triggers a test on the crowd-testing platform TestIO from a pull request by adding and editing specific comments.
If you want to do that you need to be a customer from TestIO.
Initiate test on crowd-testing platform TestIO from a pull request with specific comments. Requires TestIO customer account.

inputs:
testio-slug:
Expand Down
21 changes: 21 additions & 0 deletions resources/exploratory_test_comment_prepare_android.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"test_environment": {
"url": "your URL of preview deployment or built bundle from bot-the-builder",
"access": "provide credentials for the tester to access the environment"
},
"feature": {
"title": "The name of the feature to be tested",
"description": "A short description of the feature to be tested",
"howtofind": "Describe where to find the feature to be tested",
"user_stories": [
"Add 1 or more user stories here which you want the tester to verify"
]
},
"native": {
"android": {
"min": 8,
"max": 10
}
},
"additionalInstructions": "(optional, remove it if not needed)"
}
30 changes: 23 additions & 7 deletions resources/exploratory_test_comment_prepare_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,8 @@
},
"user_stories": {
"type": "array",
"items": [
{
"type": "string"
}
],
"minItems": 1,
"additionalItems": true
"items": { "type": "string" },
"minItems": 1
}
},
"required": [
Expand All @@ -46,10 +41,31 @@
"user_stories"
]
},
"native": {
"type": "object",
"properties": {
"android": {
"type": "object",
"properties": {
"min": {
"type": "integer",
"minimum": 8
},
"max": {
"type": "integer"
}
},
"required": [
"min"
]
}
}
},
"additionalInstructions": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"test_environment",
"feature"
Expand Down
4 changes: 4 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ sonar.organization=staffbase
sonar.sources=src/
sonar.tests=test/

# https://docs.sonarcloud.io/advanced-setup/analysis-scope/
# Exclude those .ts files which are invoked by the action and only use COMPLETELY test covered functions themselves
sonar.exclusions=src/addPRcomment.ts,src/triggerTest.ts

# required for enabling the scanner to use the generated coverage info
# https://docs.sonarcloud.io/enriching/test-coverage/javascript-typescript-test-coverage/
sonar.javascript.lcov.reportPaths=coverage/**/lcov.info
Expand Down
21 changes: 17 additions & 4 deletions src/TestIOTriggerTestGHA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export class TestIOTriggerTestGHA {

public static readonly persistedPayloadFile = 'temp/testio_payload.json';
private static readonly commentPrepareTemplateFile = "exploratory_test_comment_prepare_template.md";
private static readonly commentPrepareJsonFile = "exploratory_test_comment_prepare.json";
private static readonly commentPrepareDefaultJsonFile = "exploratory_test_comment_prepare_default.json";
private static readonly commentPrepareAndroidJsonFile = "exploratory_test_comment_prepare_android.json";

private _githubToken?: string;
private _owner?: string;
Expand Down Expand Up @@ -101,7 +102,7 @@ export class TestIOTriggerTestGHA {
return this._testioToken;
}

public async addPrepareComment(createCommentUrl: string): Promise<string> {
public async addPrepareComment(createCommentUrl: string, context: string = "default"): Promise<string> {
if (!(this.githubToken && this.repo && this.owner && this.pr)) {
const errorMessage = "Github properties are not configured";
const error = Util.prepareErrorMessageAndOptionallyThrow(errorMessage, this.errorFile, true);
Expand All @@ -111,7 +112,20 @@ export class TestIOTriggerTestGHA {
const commentPrepareTemplateFile = `${this.actionRootDir}/resources/${TestIOTriggerTestGHA.commentPrepareTemplateFile}`;
const commentTemplate = fs.readFileSync(commentPrepareTemplateFile, 'utf8');

const commentPrepareJsonFile = `${this.actionRootDir}/resources/${TestIOTriggerTestGHA.commentPrepareJsonFile}`;
let contextJsonFile: string;
switch (context) {
case "android": {
contextJsonFile = TestIOTriggerTestGHA.commentPrepareAndroidJsonFile;
break;
}

case "default":
default: {
contextJsonFile = TestIOTriggerTestGHA.commentPrepareDefaultJsonFile;
}
}

const commentPrepareJsonFile = `${this.actionRootDir}/resources/${contextJsonFile}`;
const jsonString = fs.readFileSync(commentPrepareJsonFile, 'utf8');

const requiredInformationPlaceholder = "$$REQUIRED_INFORMATION_TEMPLATE$$";
Expand Down Expand Up @@ -170,7 +184,6 @@ export class TestIOTriggerTestGHA {
if (!valid) {
if (validation.errors) {
const output = betterAjvErrors(prepareTestSchemaFile, preparation, validation.errors);
console.log(output);
Util.prepareErrorMessageAndOptionallyThrow(`Provided json is not conform to schema: ${output}`, this.errorFile);
}
Util.prepareErrorMessageAndOptionallyThrow("Provided json is not conform to schema", this.errorFile);
Expand Down
62 changes: 61 additions & 1 deletion src/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export class Util {
public static validateObjectAgainstSchema(parsedObject: any, schemaFile: string): { valid: boolean; validation: ValidateFunction<unknown> } {
const prepareTestSchema = JSON.parse(fs.readFileSync(schemaFile, 'utf8'));
const ajv = new Ajv({
strictTuples: false
strict: true,
strictSchema: true
});
const validation = ajv.compile(prepareTestSchema);
const valid = validation(parsedObject);
Expand Down Expand Up @@ -67,6 +68,34 @@ export class Util {
testing_type: "rapid"
}
};

if (prepareObject.native?.android) {
testioPayload.exploratory_test = {
...testioPayload.exploratory_test, ...{
requirements: [
{
category: {
id: 2,
name: "Smartphones"
},
operating_system: {
id: 1,
name: "Android"
},
min_operating_system_version: {
id: Util.getTestIoVersionIdFromVersionNumber("android", prepareObject.native.android.min),
name: "" + prepareObject.native.android.min
},
max_operating_system_version: (prepareObject.native.android.max ? {
id: Util.getTestIoVersionIdFromVersionNumber("android", prepareObject.native.android.max),
name: "" + prepareObject.native.android.max
} : null)
}
]
}
}
}

return testioPayload;
}

Expand Down Expand Up @@ -144,4 +173,35 @@ export class Util {
preparation = Util.getJsonObjectFromComment(jsonRegex, comment, 1);
return preparation;
}

static getTestIoVersionIdFromVersionNumber(operatingSystem: string, versionNumber: number): number | undefined {
const operatingSystemToVersionNumberToVersionIDs = new Map<string, Map<number, number>>([
["android", new Map<number, number>([
[8, 266],
[9, 319],
[10, 380],
[11, 462],
[12, 572],
[13, 661],
[14, 753],
])]
]);

const versionNumberToID: Map<number, number> | undefined = operatingSystemToVersionNumberToVersionIDs.get(operatingSystem);
if (!versionNumberToID) {
console.log(`Operating System not found: ${operatingSystem}`);
return undefined;
}

const minVersion: number = Array.from(versionNumberToID.keys()).reduce((a, b) => a < b ? a : b);
const maxVersion: number = Array.from(versionNumberToID.keys()).reduce((a, b) => a > b ? a : b);
if (versionNumber < minVersion) {
versionNumber = minVersion;
}
if (versionNumber > maxVersion) {
versionNumber = maxVersion;
}

return versionNumberToID.get(versionNumber);
}
}
4 changes: 2 additions & 2 deletions src/addPRcomment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async function addComment() {
const errorFileName = `${process.env.TESTIO_ERROR_MSG_FILE}`;

// TODO handle provided context (default, android, ios, native (= both android + native)
const context = process.argv[2]
const context = `${process.argv[2]}`;
console.log("Given context: " + context);

const gha = TestIOTriggerTestGHA.createForGithub(
Expand All @@ -33,7 +33,7 @@ async function addComment() {
errorFileName
);

await gha.addPrepareComment(`${process.env.TESTIO_CREATE_COMMENT_URL}`);
await gha.addPrepareComment(`${process.env.TESTIO_CREATE_COMMENT_URL}`, context);
}

addComment().then();
4 changes: 2 additions & 2 deletions src/reportFailure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import {Octokit} from "@octokit/rest";

async function reportFailure() {
const errorFileName = `${process.env.TESTIO_ERROR_MSG_FILE}`;
const errorMessageFilePath = `${process.env.TESTIO_SCRIPTS_DIR}/resources/${errorFileName}`;
const errorMessageFilePath = `${process.env.TESTIO_SCRIPTS_DIR}/${errorFileName}`;
const createCommentUrl = `${process.env.TESTIO_CREATE_COMMENT_URL}`;

let commentErrorMessage = "";

if (fs.existsSync(errorMessageFilePath)) {
const errorMessageToReport = fs.readFileSync(errorMessageFilePath, 'utf8');
commentErrorMessage = "🚨 Failure 🚨 :bangbang: ⛔️ Please check the following error ⛔️ :bangbang: \n\n```" + errorMessageToReport + "```";
commentErrorMessage = "🚨 Failure 🚨 :bangbang: ⛔️ Please check the following error ⛔️ :bangbang: \n\n```\n" + errorMessageToReport + "\n```";
} else {
commentErrorMessage = "🚨 Failed to trigger a test on TestIO 🚨 Please revise your steps";
}
Expand Down
1 change: 1 addition & 0 deletions src/retrievePayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async function createPayload() {
);
const commentContents = await gha.retrieveCommentContent(submitCommentID, submitCommentUrl);

// TODO move this part into gha.retrieveCommentConent
const triggerCommentUrl = Util.getUrlFromComment(commentContents);
if (triggerCommentUrl != undefined) {
core.setOutput("testio-create-comment-url", triggerCommentUrl);
Expand Down
Loading