Skip to content

Commit

Permalink
feat(website-frontend): add new repositories page (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
xandervedder authored May 24, 2024
1 parent d158615 commit 29b65dd
Show file tree
Hide file tree
Showing 86 changed files with 15,363 additions and 13,403 deletions.
26,497 changes: 14,606 additions & 11,891 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/e2e/po/home/homepage.po.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DashboardPage } from '../shared/dashboard-page.po.js';

export class Homepage extends DashboardPage {
public readonly h1 = this.page.locator('.hero p');
public readonly hero = this.page.locator('sme-hero');

public async navigate() {
return this.page.goto('/');
Expand Down
10 changes: 0 additions & 10 deletions packages/e2e/po/repositories/accordion-card.po.ts

This file was deleted.

9 changes: 0 additions & 9 deletions packages/e2e/po/repositories/accordion.po.ts

This file was deleted.

9 changes: 0 additions & 9 deletions packages/e2e/po/repositories/api-key-generator.po.ts

This file was deleted.

6 changes: 0 additions & 6 deletions packages/e2e/po/repositories/mutation-score-badge.po.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/e2e/po/repositories/repositories-list.po.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/e2e/po/repositories/repositories-page.po.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { RepositoriesListPageObject } from './repositories-list.po.js';
import { RepositoryModalDialogPageObject } from './repository-modal-dialog.po.js';
import { SelectPageObject } from '../shared/select.po.js';
import { DashboardPage } from '../shared/dashboard-page.po.js';

export class RepositoriesPage extends DashboardPage {
public readonly repositoryList = new RepositoriesListPageObject(
this.page.locator('stryker-repository-list'),
);
public readonly modalDialog = new RepositoryModalDialogPageObject(
this.page.locator('ngb-modal-window'),
);
public readonly ownerSelector = new SelectPageObject(this.page.locator('stryker-owner-selector'));

public async navigate() {
Expand Down
17 changes: 0 additions & 17 deletions packages/e2e/po/repositories/repository-modal-dialog.po.ts

This file was deleted.

23 changes: 0 additions & 23 deletions packages/e2e/po/repositories/repository-switch.po.ts

This file was deleted.

9 changes: 0 additions & 9 deletions packages/e2e/po/repositories/respositories-page.po.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { DashboardPage } from '../shared/dashboard-page.po.js';
import { RepositoriesListPageObject } from './repositories-list.po.js';
import { RepositoryModalDialogPageObject } from './repository-modal-dialog.po.js';

export class RepositoriesPage extends DashboardPage {
public readonly repositoryList = new RepositoriesListPageObject(
this.page.locator('stryker-repository-list'),
);
public readonly modalDialog = new RepositoryModalDialogPageObject(
this.page.locator('ngb-modal-window'),
);

public async navigate() {
await this.page.goto('/repositories');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e/spec/homepage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ test.describe('Homepage', () => {
});

test('should show slogan', async () => {
await expect(sut.h1).toHaveText('See your mutation testing reports from anywhere');
await expect(sut.hero).toContainText('See your mutation testing reports from anywhere');
});
});
187 changes: 79 additions & 108 deletions packages/e2e/spec/repositories-page.spec.ts
Original file line number Diff line number Diff line change
@@ -1,145 +1,116 @@
import { test, expect } from '@playwright/test';
import { test, expect, Page, Locator } from '@playwright/test';
import { RepositoriesPage } from '../po/repositories/repositories-page.po.js';
import type { RepositorySwitchPageObject } from '../po/repositories/repository-switch.po.js';
import type { Repository } from '@stryker-mutator/dashboard-contract';
import { ReportClient } from '../po/reports/report-client.po.js';
import { createContainsRegExp } from '../po/helpers.js';

// Example: 0527de29-6436-4564-9c5f-34f417ec68c0
const API_KEY_REGEX = /^[0-9a-z]{8}-(?:[0-9a-z]{4}-){3}[0-9a-z]{12}$/;

test.describe.serial('Repositories page', () => {
let page: RepositoriesPage;
let client: ReportClient;
let repositoriesPage: RepositoriesPage;
let page: Page;
let toggleModal: Locator;

const copyText: () => Promise<string> = async () =>
await page.evaluate('navigator.clipboard.readText()');

const closeToggleDialog = async () => {
await toggleModal.getByText('Close').click();
};

const openToggleDialog = async () => {
await page.locator('sme-spatious-layout').locator('#toggle-repositories').click();
};

const openBadgeDialog = async () => {
await page.locator('sme-button').getByText('Badge', { exact: true }).click();
};

test.beforeAll(async ({ browser }) => {
const context = await browser.newContext();
client = new ReportClient(context.request);
page = new RepositoriesPage(await context.newPage());
await page.logOn();
await page.navigate();
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
repositoriesPage = new RepositoriesPage(await context.newPage());
page = repositoriesPage.page;
await repositoriesPage.logOn();
await repositoriesPage.navigate();
await page.waitForSelector('sme-notify');
toggleModal = page.locator('sme-modal').first();
});

test.afterAll(async () => {
await page.logOff();
await page.close();
});

test('should list all my repos', async () => {
await expect(page.repositoryList.repositoryNamesLocator).toHaveText([
'github.com/strykermutator-test-account/hello-test',
'github.com/strykermutator-test-account/hello-world',
]);
await repositoriesPage.logOff();
await repositoriesPage.close();
});

test('should all be unchecked', async () => {
const repos = await page.repositoryList.all(2);
for (const repo of repos) {
await expect(repo.checkbox).not.toBeChecked();
}
test('should show an information box when no repositories are enabled', async () => {
expect(page.locator('sme-spatious-layout')).toContainText(
'It appears you do not have any enabled repositories, enable them by clicking on the enable repositories button.',
);
});

test('should not show the modal dialog', async () => {
await expect(page.modalDialog.host).not.toExist();
});

test.describe('owner selector', () => {
test('should show the username and organization', async () => {
await expect(page.ownerSelector.options()).toHaveCount(2);
await expect(page.ownerSelector.options().nth(0)).toHaveAttribute(
'value',
'strykermutator-test-account',
);
await expect(page.ownerSelector.options().nth(1)).toHaveAttribute(
'value',
'stryker-mutator-test-organization',
);
test.describe('when opening the toggle dialog', () => {
test.beforeAll(async () => {
await openToggleDialog();
});

test.describe('when selecting an organization', () => {
test.beforeAll(async () => {
await page.ownerSelector.select('stryker-mutator-test-organization');
});

test.afterAll(async () => {
await page.ownerSelector.select('strykermutator-test-account');
});

test("should show the repo's belonging to that organization", async () => {
await expect(page.repositoryList.repositoryNamesLocator).toContainText(
'github.com/stryker-mutator-test-organization/hello-org',
);
});
test('should open modal and show all available repositories', async () => {
expect(toggleModal).toContainText('Toggle repositories');
expect(await toggleModal.locator('sme-toggle-repository').count()).toBe(2);
});
});

test.describe('when enabling a repository', () => {
test.beforeAll(async () => {
const repos = await page.repositoryList.all(2);
await repos[0].flipSwitch();
});
test('should enable repository', async () => {
const toggleRepository = toggleModal.locator('sme-toggle-repository').first();
await toggleRepository.locator('sme-toggle-button').click();
await toggleRepository.getByText('📋').click();
const text = await copyText();

test.afterAll(async () => {
if (await page.modalDialog.isVisible()) {
await page.modalDialog.close();
}
});
await closeToggleDialog();
const repository = page.locator('sme-repository');

test('should show the modal dialog', async () => {
await expect(page.modalDialog.host).toBeVisible();
await expect(page.modalDialog.title).toHaveText('hello-test');
expect(text).toMatch(API_KEY_REGEX);
expect(repository).toContainText('hello-test');
});

test('should show the api key', async () => {
await expect(page.modalDialog.apiKeyGenerator.apiKey).toHaveText(API_KEY_REGEX);
});
test('should disable repository', async () => {
await openToggleDialog();

test('should show an explanation "About your key"', async () => {
const card = page.modalDialog.accordion.getCard('About your key');
await expect(card.body).toBeVisible();
});
const toggleRepository = toggleModal.locator('sme-toggle-repository').first();
await toggleRepository.locator('sme-toggle-button').click();
await closeToggleDialog();
await page.waitForSelector('sme-notify');

test('should hide "About your key" explanation when activated again', async () => {
const card = page.modalDialog.accordion.getCard('About your key');
await card.activate();
await expect(card.body).not.toBeVisible();
const repository = page.locator('sme-repository');
expect(repository).not.toBeVisible();
});
});

test.describe('when a repository is enabled', () => {
let repositoryPageObject: RepositorySwitchPageObject;
let repository: Repository;
test.describe('when opening the badge dialog', async () => {
let modal: Locator;

test.beforeAll(async () => {
const allRepositories = await client.getUserRepositories();
repository = allRepositories[1];
await client.enableRepository(repository.slug);
await page.navigate();
repositoryPageObject = page.repositoryList.repository(repository.name);
await openToggleDialog();
const toggleRepository = toggleModal.locator('sme-toggle-repository').first();
await toggleRepository.locator('sme-toggle-button').click();
await toggleRepository.getByText('📋').click();
await closeToggleDialog();

await openBadgeDialog();
modal = page.locator('sme-modal').filter({ hasText: 'Configure mutation badge' });
});

test('should show the mutation score badge for that repo', async () => {
await expect(repositoryPageObject.mutationScoreBadge.img).toHaveAttribute(
'src',
createContainsRegExp(encodeURIComponent(repository.slug)),
);
await expect(repositoryPageObject.mutationScoreBadge.link).toHaveAttribute(
'href',
createContainsRegExp(`reports/${repository.slug}`),
);
test('should open modal and copy mutation badge', async () => {
await modal.getByText('Copy badge').click();

const copiedText = await copyText();
expect(copiedText).toContain('style=flat');
expect(modal).toContainText('Choose from the following styles');
});

test.describe('and displayed', () => {
test.beforeAll(async () => {
await repositoryPageObject.display();
});
test('should hide the API key', async () => {
await expect(page.modalDialog.apiKeyGenerator.apiKey).toContainText('•••••••••••••••••••');
});

test('should generate a new api key if "Generate new" is clicked', async () => {
await page.modalDialog.apiKeyGenerator.generateNew();
await expect(page.modalDialog.apiKeyGenerator.apiKey).toHaveText(API_KEY_REGEX);
});
test('should copy mutation badge with differing style', async () => {
await modal.locator('sme-badge-configurator').getByLabel('plastic').click();

await modal.getByText('Copy badge').click();

const copiedText = await copyText();
expect(copiedText).toContain('style=plastic');
});
});
});
2 changes: 1 addition & 1 deletion packages/stryker-elements/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { StorybookConfig } from '@storybook/web-components-vite';

const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
framework: {
name: '@storybook/web-components-vite',
Expand Down
Loading

0 comments on commit 29b65dd

Please sign in to comment.