refactor: turn Profile page into a component

This way it is easier to inspect the design implementation.

Signed-off-by: André Jaenisch <andre.jaenisch@posteo.de>
This commit is contained in:
André Jaenisch 2024-02-19 11:33:41 +01:00
parent 34be485a49
commit 20a44974d5
No known key found for this signature in database
GPG key ID: 5A668E771F1ED854
13 changed files with 714 additions and 213 deletions

View file

@ -1,3 +1,12 @@
<button type="button" class="underline">block</button> <script lang="ts">
or import { _ } from 'svelte-i18n';
<button type="button" class="underline">report</button> </script>
{@html $_('page.profile.activities.block_or_report', {
values: {
blockElementOpen: '<button type="button" class="underline">',
blockElementClose: '</button>',
reportElementOpen: '<button type="button" class="underline">',
reportElementClose: '</button>'
}
})}

View file

@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import { _ } from 'svelte-i18n';
/** /**
* Under which name shall the person be known? * Under which name shall the person be known?
*/ */
@ -11,7 +13,8 @@
</script> </script>
<h1 class="h1 font-semibold text-4xl leading-tight"> <h1 class="h1 font-semibold text-4xl leading-tight">
<span class="sr-only">Profile for </span> <!-- FIXME: Include placeholder in i18n -->
<span class="sr-only">{$_('page.profile.heading')}</span>
{@html displayName} {@html displayName}
</h1> </h1>
<p class="leading-normal my-1">({@html pronoun})</p> <p class="leading-normal my-1">({@html pronoun})</p>

View file

@ -0,0 +1,10 @@
<script lang="ts">
import ProfileTemplate from '../templates/Profile.svelte';
/**
* Data populated by SvelteKit
*/
export let data: unknown = null;
</script>
<ProfileTemplate {data} />

View file

@ -0,0 +1,5 @@
export type ProfileData = {
user: {
display_name: string;
};
};

View file

@ -0,0 +1,105 @@
<script lang="ts">
import { _, date } from 'svelte-i18n';
import { NorthStar24, Star16 } from 'svelte-octicons';
import Avatar from '../atoms/Avatar.svelte';
import BlockOrReport from '../atoms/BlockOrReport.svelte';
import Created from '../atoms/Created.svelte';
import DisplayName from '../atoms/DisplayName.svelte';
import type { ProfileData } from './Profile.d.ts';
/**
* Required context for populating the template.
*/
export let data: ProfileData = null;
</script>
<section class="w-full mx-auto flex px-8 pt-8">
<!-- Profile header -->
<div>
<Avatar avatar={data.user.avatar} displayName={data.user.display_name} />
</div>
<!-- Board -->
<div class="flex flex-1 flex-col pl-8">
<div class="self-end">
<Created
created_at={data.user.created_at}
created_at_formatted={data.user.created_at_formatted}
/>
<BlockOrReport />
</div>
<!-- Top Row -->
<div class="flex mt-2">
<!-- Board -->
<div class="flex flex-col flex-1 gap-2">
<div class="flex items-end">
<DisplayName displayName={data.user.display_name} pronoun={data.user.pronoun} />
</div>
<span class="leading-tight">
{data.user.username}@{data.user.instance}
</span>
</div>
<!-- Interaction Links -->
<button type="button" class="btn-icon border rounded-none self-start">
<Star16 fill="currentColor" />
</button>
</div>
</div>
</section>
<section class="w-full h-full mx-auto flex mt-8 px-8">
<!-- Board -->
<div class="flex flex-1 flex-col px-4">
<div class="space-y-6">
<h2 class="h2 font-semibold leading-tight text-base px-4">
{$_('page.profile.projects.heading')}
</h2>
<p class="px-4">
{$_('page.profile.projects.empty')}
{@html $_('page.profile.projects.add_or_import', {
values: {
addElementOpen: '<a class="anchor" href="#">',
addElementClose: '</a>',
importElementOpen: '<a class="anchor" href="/projects/import/">',
importElementClose: '</a>'
}
})}
</p>
</div>
</div>
<!-- History -->
<div class="flex flex-1 flex-col px-4">
<div class="space-y-6">
<h2 class="h2 font-semibold leading-tight text-base">
{$_('page.profile.history.heading')}
</h2>
<ul>
<li class="flex flex-col border-l-2 ml-2">
<!-- Commit day -->
<span class="relative">
<span class="absolute -left-3">
<NorthStar24 fill="currentColor" />
</span>
<span class="ml-4">
{$date(new Date('2023-04-23'))} - {$_(
'page.profile.history.activities.setup.summary'
)}
</span>
</span>
<!-- Commits -->
<span class="ml-6 mt-8">
<span class="border box-decoration-clone px-4 py-2 leading-10">
{$_('page.profile.history.activities.setup.description', {
values: {
created_with: data.user.created_with,
instance: data.user.instance,
username: data.user.username
}
})}
</span>
</span>
</li>
</ul>
</div>
</div>
</section>

View file

@ -34,6 +34,27 @@
"heading": "Create project", "heading": "Create project",
"intro": "Add a new F2 project to Anvil." "intro": "Add a new F2 project to Anvil."
}, },
"profile": {
"activities": {
"block_or_report": "{blockElementOpen}block{blockElementClose} or {reportElementOpen}report{reportElementClose}",
"like": ""
},
"heading": "Profile for",
"history": {
"activities": {
"setup": {
"description": "The F2 account @{username}@{instance} was successfully set up within {created_with}",
"summary": "account set up"
}
},
"heading": "Activities"
},
"projects": {
"add_or_import": "{addElementOpen}Add a project{addElementClose} or {importElementOpen}import a project{importElementClose}.",
"empty": "No projects added yet.",
"heading": "Projects"
}
},
"welcome": "Welcome to Anvil!" "welcome": "Welcome to Anvil!"
} }
} }

View file

@ -1,77 +1,8 @@
<script> <script>
import Avatar from '$lib/components/atoms/Avatar.svelte'; import Profile from '$lib/components/pages/Profile.svelte';
import BlockOrReport from '$lib/components/atoms/BlockOrReport.svelte';
import Created from '$lib/components/atoms/Created.svelte';
import DisplayName from '$lib/components/atoms/DisplayName.svelte';
/** @type {import('./$types').PageData} */ /** @type {import('./$types').PageData} */
export let data; export let data;
</script> </script>
<section class="w-full mx-auto flex px-8 pt-8"> <Profile {data} />
<!-- Profile header -->
<div>
<Avatar avatar={data.user.avatar} displayName={data.user.display_name} />
</div>
<!-- Board -->
<div class="flex flex-1 flex-col pl-8">
<div class="self-end">
<Created
created_at={data.user.created_at}
created_at_formatted={data.user.created_at_formatted}
/>
<BlockOrReport />
</div>
<!-- Top Row -->
<div class="flex mt-2">
<!-- Board -->
<div class="flex flex-col flex-1 gap-2">
<div class="flex items-end">
<DisplayName displayName={data.user.display_name} pronoun={data.user.pronoun} />
</div>
<span class="leading-tight">
{@html data.user.username}@{@html data.user.instance}
</span>
</div>
<!-- Interaction Links -->
<button type="button" class="btn-icon border rounded-none self-start"></button>
</div>
</div>
</section>
<section class="w-full h-full mx-auto flex mt-8 px-8">
<!-- Board -->
<div class="flex flex-1 flex-col px-4">
<div class="space-y-6">
<h2 class="h2 font-semibold leading-tight text-base px-4">Projects</h2>
<p class="px-4">
No projects added yet.
<a class="anchor" href="#">Add a project</a>
or
<a class="anchor" href="/projects/import/">import a project</a>.
</p>
</div>
</div>
<!-- History -->
<div class="flex flex-1 flex-col px-4">
<div class="space-y-6">
<h2 class="h2 font-semibold leading-tight text-base">Activities</h2>
<ul>
<li class="flex flex-col border-l-2 ml-2">
<!-- Commit day -->
<span>
<!-- North Star in surface-900 -->
<span class="-ml-2 bg-[#0f161f]">✴️</span>
<span class="ml-4">Apr 23, 2023 - account set up</span>
</span>
<!-- Commits -->
<span class="ml-6 mt-8">
<span class="border box-decoration-clone px-4 py-2 leading-10">
The F2 account @{@html data.user.username}@{@html data.user.instance} was successfully
set up within {@html data.user.created_with}
</span>
</span>
</li>
</ul>
</div>
</div>
</section>

View file

@ -1,9 +1,17 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte'; import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n';
import BlockOrReport from '../../../src/lib/components/atoms/BlockOrReport.svelte'; import BlockOrReport from '../../../src/lib/components/atoms/BlockOrReport.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('BlockOrReport.svelte', () => { describe('BlockOrReport.svelte', () => {
beforeEach(() => {
register('en', () => import('../../../src/lib/i18n/locales/en.json'));
init({ fallbackLocale: 'en', initialLocale: 'en' });
locale.set('en');
});
it('should mount', () => { it('should mount', () => {
// Arrange // Arrange
// Nothing to prepare // Nothing to prepare
@ -23,7 +31,7 @@ describe('BlockOrReport.svelte', () => {
render(BlockOrReport); render(BlockOrReport);
// Assert // Assert
expect(screen.getByText('block')).toBeDefined(); expect(screen.getByRole('button', { name: 'block' })).toBeDefined();
}); });
it('should allow for report', () => { it('should allow for report', () => {
@ -34,6 +42,6 @@ describe('BlockOrReport.svelte', () => {
render(BlockOrReport); render(BlockOrReport);
// Assert // Assert
expect(screen.getByText('report')).toBeDefined(); expect(screen.getByRole('button', { name: 'report' })).toBeDefined();
}); });
}); });

View file

@ -1,9 +1,17 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte'; import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n';
import DisplayName from '../../../src/lib/components/atoms/DisplayName.svelte'; import DisplayName from '../../../src/lib/components/atoms/DisplayName.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('DisplayName.svelte', () => { describe('DisplayName.svelte', () => {
beforeEach(() => {
register('en', () => import('../../../src/lib/i18n/locales/en.json'));
init({ fallbackLocale: 'en', initialLocale: 'en' });
locale.set('en');
});
it('should mount', () => { it('should mount', () => {
// Arrange // Arrange
const displayName = 'Jane Doe'; const displayName = 'Jane Doe';
@ -13,6 +21,7 @@ describe('DisplayName.svelte', () => {
render(DisplayName, { displayName, pronoun }); render(DisplayName, { displayName, pronoun });
// Assert // Assert
expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument();
expect(screen.getByText(displayName)).toBeInTheDocument(); expect(screen.getByText(displayName)).toBeInTheDocument();
// Turn into regular expression to respect round brackets // Turn into regular expression to respect round brackets
expect(screen.getByText(new RegExp(pronoun))).toBeInTheDocument(); expect(screen.getByText(new RegExp(pronoun))).toBeInTheDocument();

View file

@ -2,7 +2,7 @@ import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte'; import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n'; import { init, locale, register } from 'svelte-i18n';
import ImportProject from '../../../src/lib/components/templates/ImportProject.svelte'; import ImportProject from '../../../src/lib/components/pages/ImportProject.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json'; import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('ImportProject.svelte', () => { describe('ImportProject.svelte', () => {
@ -14,10 +14,10 @@ describe('ImportProject.svelte', () => {
it('should mount', () => { it('should mount', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
const { container } = render(ImportProject); const { container } = render(ImportProject, { data });
// Assert // Assert
expect(container).toBeTruthy(); expect(container).toBeTruthy();
@ -25,10 +25,10 @@ describe('ImportProject.svelte', () => {
it('should have a form', () => { it('should have a form', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect(screen.getByRole('form')).toBeInTheDocument(); expect(screen.getByRole('form')).toBeInTheDocument();
@ -36,10 +36,10 @@ describe('ImportProject.svelte', () => {
it('should have a h2', () => { it('should have a h2', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const h2 = screen.getByRole('heading', { level: 2 }); const h2 = screen.getByRole('heading', { level: 2 });
// Assert // Assert
@ -49,10 +49,10 @@ describe('ImportProject.svelte', () => {
it('should have an intro text', () => { it('should have an intro text', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect(screen.getByText(enMessages.page.import_project.intro)).toBeInTheDocument(); expect(screen.getByText(enMessages.page.import_project.intro)).toBeInTheDocument();
@ -61,10 +61,10 @@ describe('ImportProject.svelte', () => {
describe('name', () => { describe('name', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the asterisk // Turn into regular expression to account for the asterisk
const name = screen.getByLabelText( const name = screen.getByLabelText(
@ -77,10 +77,10 @@ describe('ImportProject.svelte', () => {
it('should have a text input', () => { it('should have a text input', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const name = screen.getByPlaceholderText( const name = screen.getByPlaceholderText(
enMessages.page.import_project.form.fields.name.placeholder enMessages.page.import_project.form.fields.name.placeholder
@ -94,10 +94,10 @@ describe('ImportProject.svelte', () => {
describe('when empty', () => { describe('when empty', () => {
it('should display an error', () => { it('should display an error', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect( expect(
@ -110,10 +110,10 @@ describe('ImportProject.svelte', () => {
describe.skip('when filled', () => { describe.skip('when filled', () => {
it('should hide the error', () => { it('should hide the error', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect( expect(
@ -123,10 +123,11 @@ describe('ImportProject.svelte', () => {
it('should show a live updating hint', () => { it('should show a live updating hint', () => {
// Arrange // Arrange
const data = {};
const value = 'Vervis'; const value = 'Vervis';
// Act // Act
render(ImportProject, { value }); render(ImportProject, { data, value });
// Assert // Assert
expect(screen.getByText(`domain.example/projects/${value}`)).toBeInTheDocument(); expect(screen.getByText(`domain.example/projects/${value}`)).toBeInTheDocument();
@ -137,10 +138,10 @@ describe('ImportProject.svelte', () => {
describe('description', () => { describe('description', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const description = screen.getByLabelText( const description = screen.getByLabelText(
enMessages.page.import_project.form.fields.description.label enMessages.page.import_project.form.fields.description.label
@ -152,10 +153,10 @@ describe('ImportProject.svelte', () => {
it('should have a textarea', () => { it('should have a textarea', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const description = screen.getByPlaceholderText( const description = screen.getByPlaceholderText(
enMessages.page.import_project.form.fields.description.placeholder enMessages.page.import_project.form.fields.description.placeholder
@ -169,44 +170,10 @@ describe('ImportProject.svelte', () => {
describe('avatar', () => { describe('avatar', () => {
it('should have a heading', () => { it('should have a heading', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const avatar = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.avatar
});
// Assert
expect(avatar).toBeInTheDocument();
});
it('should have an upload button', () => {
// Arrange
// Nothing to prepare
// Act
render(ImportProject);
// Turn into regular expression to account for the icon
const avatar = screen.getByLabelText(
new RegExp(enMessages.page.import_project.form.fields.avatar.label)
);
// Assert
expect(avatar).toBeInTheDocument();
});
});
describe('components', () => {
it('should have a heading', () => {
// Arrange
// Nothing to prepare
// Act
render(ImportProject);
const components = screen.getByRole('heading', { const components = screen.getByRole('heading', {
level: 3, level: 3,
@ -220,10 +187,62 @@ describe('ImportProject.svelte', () => {
describe('repository', () => { describe('repository', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const avatar = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.avatar
});
// Assert
expect(avatar).toBeInTheDocument();
});
it('should have an upload button', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
// Turn into regular expression to account for the icon
const avatar = screen.getByLabelText(
new RegExp(enMessages.page.import_project.form.fields.avatar.label)
);
// Assert
expect(avatar).toBeInTheDocument();
});
});
});
describe('components', () => {
it('should have a heading', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
const components = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.components
});
// Assert
expect(components).toBeInTheDocument();
});
describe('repository', () => {
it('should have a label', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByLabelText( const repository = screen.getByLabelText(
@ -236,10 +255,10 @@ describe('ImportProject.svelte', () => {
it('should have an URL input', () => { it('should have an URL input', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByPlaceholderText( const repository = screen.getByPlaceholderText(
@ -252,10 +271,10 @@ describe('ImportProject.svelte', () => {
it('should have a hint', () => { it('should have a hint', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByText( const repository = screen.getByText(
@ -270,10 +289,10 @@ describe('ImportProject.svelte', () => {
describe('issues', () => { describe('issues', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const issues = screen.getByLabelText( const issues = screen.getByLabelText(
@ -288,10 +307,10 @@ describe('ImportProject.svelte', () => {
describe('pull requests', () => { describe('pull requests', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const issues = screen.getByLabelText(enMessages.page.import_project.form.fields.pr.label); const issues = screen.getByLabelText(enMessages.page.import_project.form.fields.pr.label);

View file

@ -0,0 +1,181 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n';
import Profile from '../../../src/lib/components/pages/Profile.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('Profile.svelte', () => {
beforeEach(() => {
register('en', () => import('../../../src/lib/i18n/locales/en.json'));
init({ fallbackLocale: 'en', initialLocale: 'en' });
locale.set('en');
});
it('should mount', () => {
// Arrange
const data = {
user: {}
};
// Act
const { container } = render(Profile, { data });
// Assert
expect(container).toBeTruthy();
});
it('should have a h1', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h1 = screen.getByRole('heading', { level: 1 });
// Assert
expect(h1).toBeInTheDocument();
expect(h1).toHaveTextContent(enMessages.page.profile.heading);
});
it('should have a block button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: 'block' });
// Assert
expect(button).toBeInTheDocument();
});
it('should have a report button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: 'report' });
// Assert
expect(button).toBeInTheDocument();
});
// FIXME: Reenable once emoji was replaced with svelte-octicon
it.skip('should have a like button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: enMessages.page.profile.activities.like });
// Assert
expect(button).toBeInTheDocument();
});
describe('projects', () => {
it('should have a h2', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h2 = screen.getByRole('heading', {
level: 2,
name: enMessages.page.profile.projects.heading
});
// Assert
expect(h2).toBeInTheDocument();
});
it('should add a project', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const a = screen.getByRole('link', { name: 'Add a project' });
// Assert
expect(a).toBeInTheDocument();
});
it('should import a project', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const a = screen.getByRole('link', { name: 'import a project' });
// Assert
expect(a).toBeInTheDocument();
expect(a).toHaveAttribute('href', '/projects/import/');
});
});
describe('history', () => {
it('should have a h2', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h2 = screen.getByRole('heading', {
level: 2,
name: enMessages.page.profile.history.heading
});
// Assert
expect(h2).toBeInTheDocument();
});
it('should have an entry for created account', () => {
// Arrange
const data = {
user: {
created_with: 'Anvil',
instance: 'domain.example',
username: 'jane_doe'
}
};
// Act
render(Profile, { data });
const li = screen.getByRole('listitem');
// Assert
expect(li).toBeInTheDocument();
expect(li).toHaveTextContent(
new RegExp(enMessages.page.profile.history.activities.setup.summary)
);
expect(li).toHaveTextContent(
new RegExp(
enMessages.page.profile.history.activities.setup.description
.replace('{username}', data.user.username)
.replace('{instance}', data.user.instance)
.replace('{created_with}', data.user.created_with)
)
);
});
});
});

View file

@ -2,7 +2,7 @@ import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte'; import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n'; import { init, locale, register } from 'svelte-i18n';
import ImportProject from '../../../src/lib/components/pages/ImportProject.svelte'; import ImportProject from '../../../src/lib/components/templates/ImportProject.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json'; import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('ImportProject.svelte', () => { describe('ImportProject.svelte', () => {
@ -14,10 +14,10 @@ describe('ImportProject.svelte', () => {
it('should mount', () => { it('should mount', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
const { container } = render(ImportProject); const { container } = render(ImportProject, { data });
// Assert // Assert
expect(container).toBeTruthy(); expect(container).toBeTruthy();
@ -25,10 +25,10 @@ describe('ImportProject.svelte', () => {
it('should have a form', () => { it('should have a form', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect(screen.getByRole('form')).toBeInTheDocument(); expect(screen.getByRole('form')).toBeInTheDocument();
@ -36,10 +36,10 @@ describe('ImportProject.svelte', () => {
it('should have a h2', () => { it('should have a h2', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const h2 = screen.getByRole('heading', { level: 2 }); const h2 = screen.getByRole('heading', { level: 2 });
// Assert // Assert
@ -49,10 +49,10 @@ describe('ImportProject.svelte', () => {
it('should have an intro text', () => { it('should have an intro text', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect(screen.getByText(enMessages.page.import_project.intro)).toBeInTheDocument(); expect(screen.getByText(enMessages.page.import_project.intro)).toBeInTheDocument();
@ -61,10 +61,10 @@ describe('ImportProject.svelte', () => {
describe('name', () => { describe('name', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the asterisk // Turn into regular expression to account for the asterisk
const name = screen.getByLabelText( const name = screen.getByLabelText(
@ -77,10 +77,10 @@ describe('ImportProject.svelte', () => {
it('should have a text input', () => { it('should have a text input', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const name = screen.getByPlaceholderText( const name = screen.getByPlaceholderText(
enMessages.page.import_project.form.fields.name.placeholder enMessages.page.import_project.form.fields.name.placeholder
@ -94,10 +94,10 @@ describe('ImportProject.svelte', () => {
describe('when empty', () => { describe('when empty', () => {
it('should display an error', () => { it('should display an error', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect( expect(
@ -110,10 +110,10 @@ describe('ImportProject.svelte', () => {
describe.skip('when filled', () => { describe.skip('when filled', () => {
it('should hide the error', () => { it('should hide the error', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Assert // Assert
expect( expect(
@ -123,10 +123,11 @@ describe('ImportProject.svelte', () => {
it('should show a live updating hint', () => { it('should show a live updating hint', () => {
// Arrange // Arrange
const data = {};
const value = 'Vervis'; const value = 'Vervis';
// Act // Act
render(ImportProject, { value }); render(ImportProject, { data, value });
// Assert // Assert
expect(screen.getByText(`domain.example/projects/${value}`)).toBeInTheDocument(); expect(screen.getByText(`domain.example/projects/${value}`)).toBeInTheDocument();
@ -137,10 +138,10 @@ describe('ImportProject.svelte', () => {
describe('description', () => { describe('description', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const description = screen.getByLabelText( const description = screen.getByLabelText(
enMessages.page.import_project.form.fields.description.label enMessages.page.import_project.form.fields.description.label
@ -152,10 +153,10 @@ describe('ImportProject.svelte', () => {
it('should have a textarea', () => { it('should have a textarea', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const description = screen.getByPlaceholderText( const description = screen.getByPlaceholderText(
enMessages.page.import_project.form.fields.description.placeholder enMessages.page.import_project.form.fields.description.placeholder
@ -169,44 +170,10 @@ describe('ImportProject.svelte', () => {
describe('avatar', () => { describe('avatar', () => {
it('should have a heading', () => { it('should have a heading', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const avatar = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.avatar
});
// Assert
expect(avatar).toBeInTheDocument();
});
it('should have an upload button', () => {
// Arrange
// Nothing to prepare
// Act
render(ImportProject);
// Turn into regular expression to account for the icon
const avatar = screen.getByLabelText(
new RegExp(enMessages.page.import_project.form.fields.avatar.label)
);
// Assert
expect(avatar).toBeInTheDocument();
});
});
describe('components', () => {
it('should have a heading', () => {
// Arrange
// Nothing to prepare
// Act
render(ImportProject);
const components = screen.getByRole('heading', { const components = screen.getByRole('heading', {
level: 3, level: 3,
@ -220,10 +187,62 @@ describe('ImportProject.svelte', () => {
describe('repository', () => { describe('repository', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
const avatar = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.avatar
});
// Assert
expect(avatar).toBeInTheDocument();
});
it('should have an upload button', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
// Turn into regular expression to account for the icon
const avatar = screen.getByLabelText(
new RegExp(enMessages.page.import_project.form.fields.avatar.label)
);
// Assert
expect(avatar).toBeInTheDocument();
});
});
});
describe('components', () => {
it('should have a heading', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
const components = screen.getByRole('heading', {
level: 3,
name: enMessages.page.import_project.form.components
});
// Assert
expect(components).toBeInTheDocument();
});
describe('repository', () => {
it('should have a label', () => {
// Arrange
const data = {};
// Act
render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByLabelText( const repository = screen.getByLabelText(
@ -236,10 +255,10 @@ describe('ImportProject.svelte', () => {
it('should have an URL input', () => { it('should have an URL input', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByPlaceholderText( const repository = screen.getByPlaceholderText(
@ -252,10 +271,10 @@ describe('ImportProject.svelte', () => {
it('should have a hint', () => { it('should have a hint', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const repository = screen.getByText( const repository = screen.getByText(
@ -270,10 +289,10 @@ describe('ImportProject.svelte', () => {
describe('issues', () => { describe('issues', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const issues = screen.getByLabelText( const issues = screen.getByLabelText(
@ -288,10 +307,10 @@ describe('ImportProject.svelte', () => {
describe('pull requests', () => { describe('pull requests', () => {
it('should have a label', () => { it('should have a label', () => {
// Arrange // Arrange
// Nothing to prepare const data = {};
// Act // Act
render(ImportProject); render(ImportProject, { data });
// Turn into regular expression to account for the icon // Turn into regular expression to account for the icon
const issues = screen.getByLabelText(enMessages.page.import_project.form.fields.pr.label); const issues = screen.getByLabelText(enMessages.page.import_project.form.fields.pr.label);

View file

@ -0,0 +1,181 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n';
import Profile from '../../../src/lib/components/templates/Profile.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('Profile.svelte', () => {
beforeEach(() => {
register('en', () => import('../../../src/lib/i18n/locales/en.json'));
init({ fallbackLocale: 'en', initialLocale: 'en' });
locale.set('en');
});
it('should mount', () => {
// Arrange
const data = {
user: {}
};
// Act
const { container } = render(Profile, { data });
// Assert
expect(container).toBeTruthy();
});
it('should have a h1', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h1 = screen.getByRole('heading', { level: 1 });
// Assert
expect(h1).toBeInTheDocument();
expect(h1).toHaveTextContent(enMessages.page.profile.heading);
});
it('should have a block button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: 'block' });
// Assert
expect(button).toBeInTheDocument();
});
it('should have a report button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: 'report' });
// Assert
expect(button).toBeInTheDocument();
});
// FIXME: Reenable once emoji was replaced with svelte-octicon
it.skip('should have a like button', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const button = screen.getByRole('button', { name: enMessages.page.profile.activities.like });
// Assert
expect(button).toBeInTheDocument();
});
describe('projects', () => {
it('should have a h2', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h2 = screen.getByRole('heading', {
level: 2,
name: enMessages.page.profile.projects.heading
});
// Assert
expect(h2).toBeInTheDocument();
});
it('should add a project', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const a = screen.getByRole('link', { name: 'Add a project' });
// Assert
expect(a).toBeInTheDocument();
});
it('should import a project', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const a = screen.getByRole('link', { name: 'import a project' });
// Assert
expect(a).toBeInTheDocument();
expect(a).toHaveAttribute('href', '/projects/import/');
});
});
describe('history', () => {
it('should have a h2', () => {
// Arrange
const data = {
user: {}
};
// Act
render(Profile, { data });
const h2 = screen.getByRole('heading', {
level: 2,
name: enMessages.page.profile.history.heading
});
// Assert
expect(h2).toBeInTheDocument();
});
it('should have an entry for created account', () => {
// Arrange
const data = {
user: {
created_with: 'Anvil',
instance: 'domain.example',
username: 'jane_doe'
}
};
// Act
render(Profile, { data });
const li = screen.getByRole('listitem');
// Assert
expect(li).toBeInTheDocument();
expect(li).toHaveTextContent(
new RegExp(enMessages.page.profile.history.activities.setup.summary)
);
expect(li).toHaveTextContent(
new RegExp(
enMessages.page.profile.history.activities.setup.description
.replace('{username}', data.user.username)
.replace('{instance}', data.user.instance)
.replace('{created_with}', data.user.created_with)
)
);
});
});
});