Skip to content

Commit

Permalink
Merge pull request #12 from playwright-community/dev
Browse files Browse the repository at this point in the history
Release 0.0.10
  • Loading branch information
estruyf authored Aug 5, 2024
2 parents 65f656b + 9cf1522 commit 6cf000d
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 17 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ src
test-results
tests
scripts
CODEOWNERS

playwright.config.ts
tsconfig.json
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file.

## [0.0.10]

- [#10](https://github.com/playwright-community/playwright-msteams-reporter/issues/10): Update to the `linkToResultsUrl` and `linkUrlOnFailure` options to support a function that returns the URL

## [0.0.9]

- [#7](https://github.com/playwright-community/playwright-msteams-reporter/issues/7): Fix for Power Automate webhook URL validation
Expand Down
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @estruyf
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ The reporter supports the following configuration options:
| `webhookUrl` | The Microsoft Teams webhook URL | `boolean` | `true` | `undefined` |
| `webhookType` | The type of the webhook (`msteams` or `powerautomate`) | `string` | `false` | `powerautomate` |
| `title` | The notification title | `string` | `false` | `Playwright Test Results` |
| `linkToResultsUrl` | Link to the test results | `string` | `false` | `undefined` |
| `linkToResultsUrl` | Link to the test results | `string \| () => string` | `false` | `undefined` |
| `linkToResultsText` | Text for the link to the test results | `string` | `false` | `View test results` |
| `linkUrlOnFailure` | Link to page where you can view, trigger, etc. the failed tests | `string` | `false` | `undefined` |
| `linkUrlOnFailure` | Link to page where you can view, trigger, etc. the failed tests | `string \| () => string` | `false` | `undefined` |
| `linkTextOnFailure` | Text for the failed tests link action | `string` | `false` | `undefined` |
| `notifyOnSuccess` | Notify on success | `boolean` | `false` | `true` |
| `mentionOnFailure` | Mention users on failure (comma separated list) | `string` | `false` | `undefined` |
Expand Down Expand Up @@ -158,6 +158,53 @@ Make sure to provide the environment variables in your Azure DevOps pipeline:
AZURE_RUN_ID: $(Build.BuildId)
```
### Combine the reporter with the Playwright Azure Reporter
You can combine the Microsoft Teams reporter with the Playwright Azure Reporter to link to create a link to the test plan results on Azure DevOps. The following example shows how you can combine both reporters:
```typescript
import { defineConfig } from "@playwright/test";
import type { AzureReporterOptions } from "@alex_neo/playwright-azure-reporter";
import type { MsTeamsReporterOptions } from "playwright-msteams-reporter";

export default defineConfig({
reporter: [
["list"],
// First define the Azure reporter
[
"@alex_neo/playwright-azure-reporter",
<AzureReporterOptions>{
...
},
],
// Then define the Microsoft Teams reporter
[
'playwright-msteams-reporter',
<MsTeamsReporterOptions>{
webhookUrl: "<webhookUrl>",
// Instead of providing the URL directly, you need to provide a function that returns the URL.
// The AZURE_PW_TEST_RUN_ID variable is only available once the Azure reporter has run.
linkToResultsUrl: () => `${process.env.AZURE_SERVER_URL}/${process.env.AZURE_PROJECT}/_testManagement/runs?runId=${process.env.AZURE_PW_TEST_RUN_ID}&_a=runCharts`,
linkToResultsText: "View test plan results",
}
]
]
});
```

Make sure to provide the environment variables in your Azure DevOps pipeline:

```yaml
- script: npx playwright test
displayName: "Run Playwright tests"
name: "playwright"
env:
CI: "true"
AZURE_SERVER_URL: $(System.CollectionUri)
AZURE_PROJECT: $(System.TeamProject)
AZURE_RUN_ID: $(Build.BuildId)
```
<br />
[![Visitors](https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fgithub.com%2Festruyf%2Fplaywright-msteams-reporter&countColor=%23263759)](https://visitorbadge.io/status?path=https%3A%2F%2Fgithub.com%2Festruyf%2Fplaywright-msteams-reporter)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "playwright-msteams-reporter",
"version": "0.0.9",
"version": "0.0.10",
"description": "Microsoft Teams reporter for Playwright which allows you to send notifications about the status of your E2E tests.",
"main": "dist/index.js",
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export interface MsTeamsReporterOptions {
webhookUrl?: string;
webhookType?: WebhookType;
title?: string;
linkToResultsUrl?: string;
linkToResultsUrl?: string | (() => string);
linkToResultsText?: string;
linkUrlOnFailure?: string;
linkUrlOnFailure?: string | (() => string);
linkTextOnFailure?: string;
notifyOnSuccess?: boolean;
mentionOnFailure?: string;
Expand Down
179 changes: 179 additions & 0 deletions src/processResults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,84 @@ describe("processResults", () => {
consoleLogSpy.mockReset();
});

it("should include the link from the function", async () => {
const fakeLink = "https://github.com/estruyf/playwright-msteams-reporter";
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (message.includes("message") && message.includes(fakeLink)) {
console.log(fakeLink);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkToResultsUrl: () => fakeLink,
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(fakeLink);

consoleLogSpy.mockReset();
});

it("should not include the link from the function when not a string (number)", async () => {
const fakeLink = 123;
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (message.includes("message") && !message.includes(fakeLink)) {
console.log(`Did not include ${fakeLink}`);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkToResultsUrl: (): any => fakeLink,
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(`Did not include ${fakeLink}`);

consoleLogSpy.mockReset();
});

it("should not include the link from the function when not a string (undefined)", async () => {
const fakeLink = undefined;
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (message.includes("message") && !message.includes(fakeLink)) {
console.log(`Did not include ${fakeLink}`);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkToResultsUrl: (): any => fakeLink,
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(`Did not include ${fakeLink}`);

consoleLogSpy.mockReset();
});

it("should include the failure link", async () => {
const fakeFailureLink =
"https://github.com/estruyf/playwright-msteams-reporter";
Expand Down Expand Up @@ -352,6 +430,107 @@ describe("processResults", () => {
consoleLogSpy.mockReset();
});

it("should include the failure link from the function", async () => {
const fakeFailureLink =
"https://github.com/estruyf/playwright-msteams-reporter";
const fakeFailureText = "View the failed tests";
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (
message.includes("message") &&
message.includes(fakeFailureLink) &&
message.includes(fakeFailureText)
) {
console.log(fakeFailureText);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkUrlOnFailure: () => fakeFailureLink,
linkTextOnFailure: "View the failed tests",
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(fakeFailureText);

consoleLogSpy.mockReset();
});

it("should not include the failure link from the function when not a string (number)", async () => {
const fakeFailureLink = 123;
const fakeFailureText = "View the failed tests";
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (
message.includes("message") &&
!message.includes(fakeFailureLink) &&
!message.includes(fakeFailureText)
) {
console.log(`Did not include ${fakeFailureLink}`);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkUrlOnFailure: (): any => fakeFailureLink,
linkTextOnFailure: "View the failed tests",
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(
`Did not include ${fakeFailureLink}`
);

consoleLogSpy.mockReset();
});

it("should not include the failure link from the function when not a string (undefined)", async () => {
const fakeFailureLink = undefined;
const fakeFailureText = "View the failed tests";
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation((message) => {
if (
message.includes("message") &&
!message.includes(fakeFailureLink) &&
!message.includes(fakeFailureText)
) {
console.log(`Did not include ${fakeFailureLink}`);
}
});
const fetchMock = jest
.fn()
.mockResolvedValue({ ok: true, text: () => "1" });
global.fetch = fetchMock;
const options: MsTeamsReporterOptions = {
...DEFAULT_OPTIONS,
webhookUrl: FLOW_WEBHOOK_URL,
webhookType: "powerautomate",
linkUrlOnFailure: (): any => fakeFailureLink,
linkTextOnFailure: "View the failed tests",
debug: true,
};
await processResults(SUITE_MOCK_FAILED as any, options);
expect(consoleLogSpy).toHaveBeenCalledWith(
`Did not include ${fakeFailureLink}`
);

consoleLogSpy.mockReset();
});

it("should show debug message", async () => {
const consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
const fetchMock = jest
Expand Down
38 changes: 28 additions & 10 deletions src/processResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,37 @@ export const processResults = async (

// Get the github actions run URL
if (options.linkToResultsUrl) {
adaptiveCard.actions.push({
type: "Action.OpenUrl",
title: options.linkToResultsText,
url: options.linkToResultsUrl,
});
let linkToResultsUrl: string;
if (typeof options.linkToResultsUrl === "string") {
linkToResultsUrl = options.linkToResultsUrl;
} else {
linkToResultsUrl = options.linkToResultsUrl();
}

if (linkToResultsUrl && typeof linkToResultsUrl === "string") {
adaptiveCard.actions.push({
type: "Action.OpenUrl",
title: options.linkToResultsText,
url: linkToResultsUrl,
});
}
}

if (!isSuccess && options.linkTextOnFailure && options.linkUrlOnFailure) {
adaptiveCard.actions.push({
type: "Action.OpenUrl",
title: options.linkTextOnFailure,
url: options.linkUrlOnFailure,
});
let linkUrlOnFailure: string;
if (typeof options.linkUrlOnFailure === "string") {
linkUrlOnFailure = options.linkUrlOnFailure;
} else {
linkUrlOnFailure = options.linkUrlOnFailure();
}

if (linkUrlOnFailure && typeof linkUrlOnFailure === "string") {
adaptiveCard.actions.push({
type: "Action.OpenUrl",
title: options.linkTextOnFailure,
url: linkUrlOnFailure,
});
}
}

if (options.webhookType === "powerautomate") {
Expand Down

0 comments on commit 6cf000d

Please sign in to comment.