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

Allow tests to contain multiple test issue keys #357

Merged
merged 6 commits into from
Aug 4, 2024
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
77 changes: 77 additions & 0 deletions src/cypress/tasks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,40 @@ describe(path.relative(process.cwd(), __filename), () => {
});
});

it("handles single outgoing requests for tests with multiple issue keys", () => {
const evidenceCollection = new SimpleEvidenceCollection();
const logger = getMockedLogger();
const listener = new tasks.PluginTaskListener("CYP", evidenceCollection, logger);
listener[tasks.PluginTask.OUTGOING_REQUEST]({
filename: "outgoingRequest.json",
request: {
url: "https://example.org",
},
test: "This is a test CYP-123 CYP-124 CYP-125",
});
expect(evidenceCollection.getEvidence("CYP-123")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJ1cmwiOiAiaHR0cHM6Ly9leGFtcGxlLm9yZyIKfQ==",
filename: "outgoingRequest.json",
},
]);
expect(evidenceCollection.getEvidence("CYP-124")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJ1cmwiOiAiaHR0cHM6Ly9leGFtcGxlLm9yZyIKfQ==",
filename: "outgoingRequest.json",
},
]);
expect(evidenceCollection.getEvidence("CYP-125")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJ1cmwiOiAiaHR0cHM6Ly9leGFtcGxlLm9yZyIKfQ==",
filename: "outgoingRequest.json",
},
]);
});

it("handles multiple outgoing requests for tests with the same issue key", () => {
const evidenceCollection = sinon.stub(new SimpleEvidenceCollection());
const logger = getMockedLogger();
Expand Down Expand Up @@ -281,6 +315,49 @@ describe(path.relative(process.cwd(), __filename), () => {
});
});

it("handles single incoming responses for tests with multiple issue keys", () => {
const evidenceCollection = new SimpleEvidenceCollection();
const logger = getMockedLogger();
const listener = new tasks.PluginTaskListener("CYP", evidenceCollection, logger);
listener[tasks.PluginTask.INCOMING_RESPONSE]({
filename: "incomingResponse.json",
response: {
allRequestResponses: [],
body: "This is example text",
duration: 12345,
headers: {
["Content-Type"]: "text/plain",
},
isOkStatusCode: true,
requestHeaders: { ["Accept"]: "text/plain" },
status: 200,
statusText: "Ok",
},
test: "This is a test CYP-123 CYP-124 CYP-125",
});
expect(evidenceCollection.getEvidence("CYP-123")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJhbGxSZXF1ZXN0UmVzcG9uc2VzIjogW10sCiAgImJvZHkiOiAiVGhpcyBpcyBleGFtcGxlIHRleHQiLAogICJkdXJhdGlvbiI6IDEyMzQ1LAogICJoZWFkZXJzIjogewogICAgIkNvbnRlbnQtVHlwZSI6ICJ0ZXh0L3BsYWluIgogIH0sCiAgImlzT2tTdGF0dXNDb2RlIjogdHJ1ZSwKICAicmVxdWVzdEhlYWRlcnMiOiB7CiAgICAiQWNjZXB0IjogInRleHQvcGxhaW4iCiAgfSwKICAic3RhdHVzIjogMjAwLAogICJzdGF0dXNUZXh0IjogIk9rIgp9",
filename: "incomingResponse.json",
},
]);
expect(evidenceCollection.getEvidence("CYP-124")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJhbGxSZXF1ZXN0UmVzcG9uc2VzIjogW10sCiAgImJvZHkiOiAiVGhpcyBpcyBleGFtcGxlIHRleHQiLAogICJkdXJhdGlvbiI6IDEyMzQ1LAogICJoZWFkZXJzIjogewogICAgIkNvbnRlbnQtVHlwZSI6ICJ0ZXh0L3BsYWluIgogIH0sCiAgImlzT2tTdGF0dXNDb2RlIjogdHJ1ZSwKICAicmVxdWVzdEhlYWRlcnMiOiB7CiAgICAiQWNjZXB0IjogInRleHQvcGxhaW4iCiAgfSwKICAic3RhdHVzIjogMjAwLAogICJzdGF0dXNUZXh0IjogIk9rIgp9",
filename: "incomingResponse.json",
},
]);
expect(evidenceCollection.getEvidence("CYP-125")).to.deep.eq([
{
contentType: "application/json",
data: "ewogICJhbGxSZXF1ZXN0UmVzcG9uc2VzIjogW10sCiAgImJvZHkiOiAiVGhpcyBpcyBleGFtcGxlIHRleHQiLAogICJkdXJhdGlvbiI6IDEyMzQ1LAogICJoZWFkZXJzIjogewogICAgIkNvbnRlbnQtVHlwZSI6ICJ0ZXh0L3BsYWluIgogIH0sCiAgImlzT2tTdGF0dXNDb2RlIjogdHJ1ZSwKICAicmVxdWVzdEhlYWRlcnMiOiB7CiAgICAiQWNjZXB0IjogInRleHQvcGxhaW4iCiAgfSwKICAic3RhdHVzIjogMjAwLAogICJzdGF0dXNUZXh0IjogIk9rIgp9",
filename: "incomingResponse.json",
},
]);
});

it("handles multiple incoming responses for tests with the same issue key", () => {
const evidenceCollection = sinon.stub(new SimpleEvidenceCollection());
const logger = getMockedLogger();
Expand Down
30 changes: 17 additions & 13 deletions src/cypress/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EvidenceCollection } from "../context";
import { getNativeTestIssueKey } from "../hooks/after/util";
import { getTestIssueKeys } from "../hooks/after/util";
import { encode } from "../util/base64";
import { dedent } from "../util/dedent";
import { errorMessage } from "../util/errors";
Expand Down Expand Up @@ -148,12 +148,14 @@ export class PluginTaskListener implements TaskListener {
args: PluginTaskParameterType[PluginTask.OUTGOING_REQUEST]
) {
try {
const issueKey = getNativeTestIssueKey(args.test, this.projectKey);
this.evidenceCollection.addEvidence(issueKey, {
contentType: "application/json",
data: encode(JSON.stringify(args.request, null, 2)),
filename: args.filename,
});
const issueKeys = getTestIssueKeys(args.test, this.projectKey);
for (const issueKey of issueKeys) {
this.evidenceCollection.addEvidence(issueKey, {
contentType: "application/json",
data: encode(JSON.stringify(args.request, null, 2)),
filename: args.filename,
});
}
} catch (error: unknown) {
if (!this.ignoredTests.has(args.test)) {
this.logger.message(
Expand All @@ -176,12 +178,14 @@ export class PluginTaskListener implements TaskListener {
args: PluginTaskParameterType[PluginTask.INCOMING_RESPONSE]
) {
try {
const issueKey = getNativeTestIssueKey(args.test, this.projectKey);
this.evidenceCollection.addEvidence(issueKey, {
contentType: "application/json",
data: encode(JSON.stringify(args.response, null, 2)),
filename: args.filename,
});
const issueKeys = getTestIssueKeys(args.test, this.projectKey);
for (const issueKey of issueKeys) {
this.evidenceCollection.addEvidence(issueKey, {
contentType: "application/json",
data: encode(JSON.stringify(args.response, null, 2)),
filename: args.filename,
});
}
} catch (error: unknown) {
if (!this.ignoredTests.has(args.test)) {
this.logger.message(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,36 +531,14 @@ describe(path.relative(process.cwd(), __filename), () => {
expect(features).to.have.length(0);
});

it("skips scenarios with multiple tags", async () => {
it("includes scenarios with multiple tags", async () => {
const logger = getMockedLogger();
const cucumberReport: CucumberMultipartFeature[] = JSON.parse(
fs.readFileSync(
"./test/resources/fixtures/xray/requests/importExecutionCucumberMultipartMultipleTags.json",
"utf-8"
)
) as CucumberMultipartFeature[];
logger.message
.withArgs(
Level.WARNING,
dedent(`
Skipping result upload for scenario: Doing stuff

Multiple test issue keys found in tags of scenario: Doing stuff
The plugin cannot decide for you which one to use:

@TestName:CYP-123 @TestName:CYP-456
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
Scenario: Doing stuff
When I prepare something
...

For more information, visit:
- https://docs.getxray.app/display/XRAYCLOUD/Importing+Cucumber+Tests+-+REST+v2
- https://qytera-gmbh.github.io/projects/cypress-xray-plugin/section/guides/targetingExistingIssues/
`)
)
.onFirstCall()
.returns();
const command = new ConvertCucumberFeaturesCommand(
{
cucumber: { prefixes: { test: "TestName:" } },
Expand All @@ -575,7 +553,17 @@ describe(path.relative(process.cwd(), __filename), () => {
new ConstantCommand(logger, cucumberReport)
);
const features = await command.compute();
expect(features).to.have.length(0);
expect(features).to.have.length(1);
expect(features[0].elements[0].tags).to.deep.eq([
{
line: 4,
name: "@TestName:CYP-123",
},
{
line: 4,
name: "@TestName:CYP-456",
},
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import {
CucumberMultipartTag,
} from "../../../../../types/xray/requests/import-execution-cucumber-multipart";
import { dedent } from "../../../../../util/dedent";
import {
errorMessage,
missingTestKeyInCucumberScenarioError,
multipleTestKeysInCucumberScenarioError,
} from "../../../../../util/errors";
import { errorMessage, missingTestKeyInCucumberScenarioError } from "../../../../../util/errors";
import { Level, Logger } from "../../../../../util/logging";
import { Command, Computable } from "../../../../command";
import { getScenarioTagRegex } from "../../../../preprocessor/commands/parsing/scenario";
Expand Down Expand Up @@ -141,20 +137,6 @@ export class ConvertCucumberFeaturesCommand extends Command<
// We know the regex: the match will contain the value in the first group.
issueKeys.push(matches[1]);
}
if (issueKeys.length > 1) {
throw multipleTestKeysInCucumberScenarioError(
{
keyword: element.keyword,
name: element.name,
steps: element.steps.map((step: CucumberMultipartStep) => {
return { keyword: step.keyword, text: step.name };
}),
},
element.tags,
issueKeys,
this.parameters.useCloudTags === true
);
}
}
if (issueKeys.length === 0) {
throw missingTestKeyInCucumberScenarioError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ describe(path.relative(process.cwd(), __filename), () => {
});

describe("<13", () => {
it("converts test results into xray info json", async () => {
it("converts test results into xray results json", async () => {
const logger = getMockedLogger();
const result: CypressRunResult_V12 = JSON.parse(
const result = JSON.parse(
readFileSync("./test/resources/runResultExistingTestIssues.json", "utf-8")
) as CypressRunResult_V12;
const command = new ConvertCypressTestsCommand(
Expand Down Expand Up @@ -83,10 +83,64 @@ describe(path.relative(process.cwd(), __filename), () => {
},
]);
});

it("converts test results with multiple issue keys into xray results json", async () => {
const logger = getMockedLogger();
const result = JSON.parse(
readFileSync(
"./test/resources/runResultExistingTestIssuesMultiple.json",
"utf-8"
)
) as CypressRunResult_V12;
const command = new ConvertCypressTestsCommand(
{ ...options, evidenceCollection: new SimpleEvidenceCollection() },
logger,
new ConstantCommand(logger, result)
);
const json = await command.compute();
expect(json).to.deep.eq([
{
evidence: [
{
data: "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAoSURBVBhXY/iPA4AkGBig0hAGlISz4AwUCTggWgJIwhlESGAB//8DAAF4fYMJdJTzAAAAAElFTkSuQmCC",
filename: "small.png",
},
],
finish: "2022-11-28T17:41:15Z",
start: "2022-11-28T17:41:15Z",
status: "PASS",
testKey: "CYP-123",
},
{
evidence: [
{
data: "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAoSURBVBhXY/iPA4AkGBig0hAGlISz4AwUCTggWgJIwhlESGAB//8DAAF4fYMJdJTzAAAAAElFTkSuQmCC",
filename: "small.png",
},
],
finish: "2022-11-28T17:41:15Z",
start: "2022-11-28T17:41:15Z",
status: "PASS",
testKey: "CYP-124",
},
{
evidence: [
{
data: "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAoSURBVBhXY/iPA4AkGBig0hAGlISz4AwUCTggWgJIwhlESGAB//8DAAF4fYMJdJTzAAAAAElFTkSuQmCC",
filename: "small.png",
},
],
finish: "2022-11-28T17:41:15Z",
start: "2022-11-28T17:41:15Z",
status: "PASS",
testKey: "CYP-125",
},
]);
});
});

describe(">=13", () => {
it("converts test results into xray info json", async () => {
it("converts test results into xray results json", async () => {
const logger = getMockedLogger();
const result: CypressCommandLine.CypressRunResult = JSON.parse(
readFileSync("./test/resources/runResult_13_0_0.json", "utf-8")
Expand Down Expand Up @@ -137,6 +191,60 @@ describe(path.relative(process.cwd(), __filename), () => {
]);
});

it("converts test results with multiple issue keys into xray results json", async () => {
const logger = getMockedLogger();
const result: CypressCommandLine.CypressRunResult = JSON.parse(
readFileSync(
"./test/resources/runResult_13_0_0_multipleTestIssueKeys.json",
"utf-8"
)
) as CypressCommandLine.CypressRunResult;
const command = new ConvertCypressTestsCommand(
{ ...options, evidenceCollection: new SimpleEvidenceCollection() },
logger,
new ConstantCommand(logger, result)
);
const json = await command.compute();
expect(json).to.deep.eq([
{
finish: "2023-09-09T10:59:29Z",
start: "2023-09-09T10:59:28Z",
status: "PASS",
testKey: "CYP-452",
},
{
evidence: [
{
data: "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAoSURBVBhXY/iPA4AkGBig0hAGlISz4AwUCTggWgJIwhlESGAB//8DAAF4fYMJdJTzAAAAAElFTkSuQmCC",
filename: "small CYP-123 CYP-125.png",
},
],
finish: "2023-09-09T10:59:29Z",
start: "2023-09-09T10:59:29Z",
status: "FAIL",
testKey: "CYP-123",
},
{
finish: "2023-09-09T10:59:29Z",
start: "2023-09-09T10:59:29Z",
status: "FAIL",
testKey: "CYP-124",
},
{
evidence: [
{
data: "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAoSURBVBhXY/iPA4AkGBig0hAGlISz4AwUCTggWgJIwhlESGAB//8DAAF4fYMJdJTzAAAAAElFTkSuQmCC",
filename: "small CYP-123 CYP-125.png",
},
],
finish: "2023-09-09T10:59:29Z",
start: "2023-09-09T10:59:29Z",
status: "FAIL",
testKey: "CYP-125",
},
]);
});

it("warns about non-attributable screenshots", async () => {
const logger = getMockedLogger();
const result: CypressCommandLine.CypressRunResult = JSON.parse(
Expand Down
Loading