Skip to content

Commit

Permalink
chore: add basic smoke test for mattermost with sso (#65)
Browse files Browse the repository at this point in the history
Fixes #75
  • Loading branch information
zachariahmiller committed May 21, 2024
1 parent 9228ee8 commit 074749c
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,10 @@ jobs:
uses: defenseunicorns/uds-common/.github/actions/save-logs@b2e8b25930c953ef893e7c787fe350f0d8679ee2 # v0.4.2
with:
suffix: ${{ matrix.type }}-${{ matrix.flavor }}-${{ github.run_id }}-${{ github.run_attempt }}

- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
if: always()
with:
name: playwright-report-${{ matrix.type }}-${{ matrix.flavor }}-${{ github.run_id }}-${{ github.run_attempt }}
path: tests/.playwright/reports/
retention-days: 30
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ terraform.tfstate.backup

# SOPS stuff that should never be committed to the repo
secret-sops-gpg.yaml

# Tests
node_modules/
.playwright/
6 changes: 6 additions & 0 deletions tasks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,22 @@ tasks:
- task: create-mm-test-bundle
- task: setup:k3d-test-cluster
- task: deploy:test-bundle
- task: setup:create-doug-user
- task: test:health-check
- task: test:ingress
- task: test:ui

- name: test-upgrade
description: Test an upgrade from the latest released package to the current branch
actions:
- task: create-mm-latest-release-bundle
- task: setup:k3d-test-cluster
- task: deploy:test-bundle
- task: setup:create-doug-user
- task: create-mm-test-bundle
- task: deploy:test-bundle
- cmd: ./uds zarf tools kubectl delete pods --all --namespace mattermost
description: "Workaround - restart the pods so the sso configuration will take effect"
- task: test:health-check
- task: test:ingress
- task: test:ui
10 changes: 10 additions & 0 deletions tasks/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ tasks:
protocol: https
address: chat.uds.dev
code: 200

- name: ui
description: Mattermost UI Checks
actions:
- cmd: npm ci
dir: tests
- cmd: npx playwright install --with-deps
dir: tests
- cmd: npx playwright test
dir: tests
40 changes: 40 additions & 0 deletions tests/auth.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { test as setup, expect } from '@playwright/test';
import { authFile } from './playwright.config';

setup('authenticate', async ({ page, context, baseURL }) => {
await page.goto('/login');

await page.getByRole("link", { name: "View in Browser" }).click();
await page.getByRole("link", { name: "Gitlab Icon GitLab" }).click();
await page.getByLabel("Username or email").fill("doug");
await page.getByLabel("Password").fill("unicorn123!@#");
await page.getByRole("button", { name: "Log In" }).click();

// ensure auth cookies were set
const cookies = await context.cookies();
const keycloakCookie = cookies.find(
(cookie) => cookie.name === "KEYCLOAK_SESSION",
);

expect(keycloakCookie).toBeDefined();
expect(keycloakCookie?.value).not.toBe("");
expect(keycloakCookie?.domain).toContain("sso.");

await page.context().storageState({ path: authFile });

await page.waitForURL(url =>
url.pathname === '/' ||
url.pathname === '/preparing-workspace' ||
url.pathname === '/unicorns/channels/town-square'
, { waitUntil: 'networkidle' });

// one-time workspace setup (when login redirects to "/preparing-workspace")
if (page.url().endsWith('/preparing-workspace')) {
const orgInput = page.getByPlaceholder("Organization name");
await orgInput.isVisible();
await orgInput.fill("Unicorns");
await page.getByTestId("continue").click();
await page.getByRole("button", { name: "Skip" }).click();
await page.getByRole("button", { name: "Finish setup" }).click();
};
})
104 changes: 104 additions & 0 deletions tests/mattermost.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { test, expect, request, APIRequestContext, Page } from "@playwright/test";
import consumers from 'stream/consumers';

type Channel = { id: string; name: string; display_name: string; };

let apiCtx: APIRequestContext;
let channel: Channel;

test.beforeEach(async ({ context, baseURL }) => {
const cookies = await context.cookies();
const token = cookies.find(c => c.name === 'MMAUTHTOKEN');

expect(token?.value).toBeDefined();

apiCtx = await request.newContext({
baseURL,
extraHTTPHeaders: {
'Accept': 'application/json',
'Authorization': `Bearer ${token?.value}`,
'X-Requested-With': 'XMLHttpRequest', // without this you get "Invalid or expired session, please login again."
}
});

// poll channel list in case default channels haven't been created yet
await expect(async () => {
const channels = await apiCtx.get('/api/v4/channels')
.then(res => res.json());

expect(channels.length).toBeGreaterThanOrEqual(1);

channel = channels.find((c: Channel) => c.display_name === 'Town Square');
expect(channel).toBeDefined();
expect(channel.display_name).toBe('Town Square');
}).toPass({
timeout: 60_000,
});
});

function randomMessage(extra: string = "") {
return `hello universe ${Math.floor((Math.random() * 1000))}-C3!` + extra;
}

async function createPost(page: Page, data: { channel_id: string, message: string; file_ids?: string[] }) {
const req = await apiCtx.post('/api/v4/posts', {
data,
});

expect(req).toBeOK();

const post = await req.json();

expect(post.id).toBeDefined();
expect(post.message).toBe(data.message);

return post;
}

test("send a message", async ({ page }) => {
const post = await createPost(page, {
channel_id: channel.id,
message: randomMessage(),
});

await page.goto('/unicorns/channels/town-square');

const el = page.locator(`#post_${post.id}`);
await expect(el).toContainText(post.message);
});

test("send a message with attachment", async ({ page }) => {
const file = {
name: 'README.md',
mimeType: 'text/plain',
buffer: Buffer.from('# My Cool Project'),
};

const upload = await apiCtx.fetch('/api/v4/files', {
method: 'POST',
multipart: {
channel_id: channel.id,
files: file,
},
});

expect(upload).toBeOK();

const res = await upload.json();

expect(res.file_infos.length).toBe(1);
expect(res.file_infos[0].name).toBe(file.name);

const message = randomMessage('\ncheckout this attachment...');
const post = await createPost(page, {
channel_id: channel.id,
message,
file_ids: [ res.file_infos[0].id ],
});

await page.goto('/unicorns/channels/town-square');

const el = page.locator(`#post_${post.id}`);
await expect(el).toContainText(post.message);

});
104 changes: 104 additions & 0 deletions tests/package-lock.json

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

9 changes: 9 additions & 0 deletions tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "uds-package-mattermost",
"license": "Apache-2.0",
"devDependencies": {
"@playwright/test": "^1.43.1",
"@types/node": "^20.12.12",
"typescript": "^5.4.5"
}
}
43 changes: 43 additions & 0 deletions tests/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { defineConfig, devices } from '@playwright/test';

export const playwrightDir = '.playwright';
export const authFile = `${playwrightDir}/auth/user.json`;

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
fullyParallel: true,
forbidOnly: !!process.env.CI, // fail CI if you accidently leave `test.only` in source
retries: process.env.CI ? 1 : 0,
workers: 1,
reporter: [
// Reporter to use. See https://playwright.dev/docs/test-reporters
['html', { outputFolder: `${playwrightDir}/reports`, open: 'never' }],
['json', { outputFile: `${playwrightDir}/reports/test-results.json`, open: 'never' }],
['list']
],

outputDir: `${playwrightDir}/output`,

use: {
baseURL: process.env.BASE_URL || 'https://chat.uds.dev', // for `await page.goto('/')` etc
trace: 'on-first-retry', // collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer
},

projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ }, // authentication

...[
'Desktop Chrome',
'Desktop Firefox',
].map((p) => ({
name: devices[p].defaultBrowserType,
dependencies: ['setup'],
use: {
...devices[p],
storageState: authFile,
},
})),
],
});
10 changes: 10 additions & 0 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

0 comments on commit 074749c

Please sign in to comment.