fix(KAN-14): preserve template inheritance and project data
This commit is contained in:
@@ -222,6 +222,10 @@ if (serve) {
|
||||
assert.match(admin, /Board columns · inherited/);
|
||||
assert.match(admin, /Ticket types · overridden/);
|
||||
assert.match(
|
||||
await render('/projects/7', { create: true, manage: true }),
|
||||
/href="\/projects\/7\/templates\/create"/,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
await render('/projects/7', { create: true }),
|
||||
/href="\/projects\/7\/templates\/create"/,
|
||||
);
|
||||
|
||||
@@ -46,6 +46,7 @@ try {
|
||||
'/project-templates?reset',
|
||||
'/projects/1/templates/create?reset',
|
||||
'/project-templates/2/edit?reset',
|
||||
'/project-templates/create?reset',
|
||||
]) {
|
||||
await visit(path);
|
||||
await noOverflow();
|
||||
@@ -73,6 +74,7 @@ try {
|
||||
});
|
||||
}
|
||||
}
|
||||
await visit('/project-templates/2/edit?reset');
|
||||
assert.equal(await page.locator('#slug').isDisabled(), true);
|
||||
assert.equal(
|
||||
await button('Save template changes').isDisabled(),
|
||||
@@ -193,6 +195,14 @@ try {
|
||||
name: 'Updated foundation',
|
||||
copy_areas: ['project_settings', 'ticket_types'],
|
||||
confirm_linked_projects_update: true,
|
||||
configuration_snapshot: await page.evaluate(
|
||||
() =>
|
||||
JSON.parse(
|
||||
document
|
||||
.querySelector('[data-page]')
|
||||
.getAttribute('data-page'),
|
||||
).props.template.configuration_snapshot,
|
||||
),
|
||||
});
|
||||
await page
|
||||
.getByRole('heading', { name: 'Project templates', exact: true })
|
||||
@@ -207,15 +217,92 @@ try {
|
||||
assert.deepEqual((await unlinked).postDataJSON(), {
|
||||
name: 'Foundation',
|
||||
copy_areas: ['project_settings', 'ticket_types'],
|
||||
configuration_snapshot: await page.evaluate(
|
||||
() =>
|
||||
JSON.parse(
|
||||
document
|
||||
.querySelector('[data-page]')
|
||||
.getAttribute('data-page'),
|
||||
).props.template.configuration_snapshot,
|
||||
),
|
||||
});
|
||||
await page
|
||||
.getByRole('heading', { name: 'Project templates', exact: true })
|
||||
.waitFor();
|
||||
await visit('/project-templates/2/edit?reset&missing');
|
||||
await visit('/project-templates/2/edit?reset&missing&unlinked');
|
||||
assert.equal(
|
||||
await page
|
||||
.getByText('This template cannot be refreshed', { exact: true })
|
||||
.count(),
|
||||
0,
|
||||
);
|
||||
assert.equal(await button('Save template changes').isEnabled(), true);
|
||||
await button('Save template changes').click();
|
||||
await page
|
||||
.getByText('This template cannot be refreshed', { exact: true })
|
||||
.getByRole('heading', { name: 'Project templates', exact: true })
|
||||
.waitFor();
|
||||
|
||||
await visit('/project-templates/create?reset');
|
||||
await page.locator('#name').fill('New independent template');
|
||||
await page.locator('#slug').fill('independent-template');
|
||||
const columns = page
|
||||
.locator('details')
|
||||
.filter({ has: page.locator('summary', { hasText: 'Board columns' }) });
|
||||
await columns
|
||||
.getByRole('button', { name: 'Add entry', exact: true })
|
||||
.click();
|
||||
await columns.getByLabel('Name', { exact: true }).fill('Todo');
|
||||
const statuses = page.locator('details').filter({
|
||||
has: page.locator('summary', { hasText: 'Ticket statuses' }),
|
||||
});
|
||||
await statuses
|
||||
.getByRole('button', { name: 'Add entry', exact: true })
|
||||
.click();
|
||||
await statuses.getByLabel('Name', { exact: true }).last().fill('Review');
|
||||
await statuses
|
||||
.getByLabel('Board column', { exact: true })
|
||||
.last()
|
||||
.selectOption({ label: 'Todo' });
|
||||
const types = page
|
||||
.locator('details')
|
||||
.filter({ has: page.locator('summary', { hasText: 'Ticket types' }) });
|
||||
await types
|
||||
.getByRole('button', { name: 'Move down', exact: true })
|
||||
.first()
|
||||
.click();
|
||||
const standaloneCreated = page.waitForRequest(
|
||||
(request) => request.method() === 'POST',
|
||||
);
|
||||
await button('Save new template').click();
|
||||
const body = (await standaloneCreated).postDataJSON();
|
||||
assert.equal(
|
||||
new URL((await standaloneCreated).url()).pathname,
|
||||
'/project-templates',
|
||||
);
|
||||
assert.equal(body.configuration_snapshot.ticket_statuses[1].name, 'Review');
|
||||
assert.equal(
|
||||
body.configuration_snapshot.ticket_statuses[1].board_column_key,
|
||||
body.configuration_snapshot.board_columns[0].configuration_key,
|
||||
);
|
||||
assert.deepEqual(
|
||||
body.configuration_snapshot.ticket_types.map((item) => item.name),
|
||||
['Release', 'Task'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
body.configuration_snapshot.ticket_types.map((item) => item.position),
|
||||
[1, 2],
|
||||
);
|
||||
assert.equal(
|
||||
new Set(
|
||||
body.configuration_snapshot.ticket_types.map(
|
||||
(item) => item.configuration_key,
|
||||
),
|
||||
).size,
|
||||
2,
|
||||
);
|
||||
await page
|
||||
.getByRole('heading', { name: 'Project templates', exact: true })
|
||||
.waitFor();
|
||||
assert.equal(await button('Save template changes').isDisabled(), true);
|
||||
|
||||
await visit('/projects/1/templates/create?reset');
|
||||
await page.locator('#name').fill('Unsaved work');
|
||||
|
||||
@@ -43,10 +43,54 @@ const template = {
|
||||
is_published: true,
|
||||
source_project: project,
|
||||
copy_areas: ['project_settings', 'ticket_types'],
|
||||
configuration_snapshot: {
|
||||
project_settings: { show_author: true, show_story_points: true },
|
||||
board_columns: [],
|
||||
ticket_statuses: [
|
||||
{
|
||||
configuration_key: '10000000-0000-4000-8000-000000000001',
|
||||
name: 'Backlog',
|
||||
position: 1,
|
||||
board_column_key: null,
|
||||
},
|
||||
],
|
||||
status_transitions: [
|
||||
{
|
||||
status_key: '10000000-0000-4000-8000-000000000001',
|
||||
target_keys: [],
|
||||
},
|
||||
],
|
||||
ticket_types: [
|
||||
{
|
||||
configuration_key: '20000000-0000-4000-8000-000000000001',
|
||||
name: 'Task',
|
||||
position: 1,
|
||||
is_parent: false,
|
||||
},
|
||||
{
|
||||
configuration_key: '20000000-0000-4000-8000-000000000002',
|
||||
name: 'Release',
|
||||
position: 2,
|
||||
is_parent: true,
|
||||
},
|
||||
],
|
||||
ticket_priorities: [
|
||||
{
|
||||
configuration_key: '30000000-0000-4000-8000-000000000001',
|
||||
name: 'Medium',
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
ticket_fields: [],
|
||||
ticket_link_types: [],
|
||||
repository_connections: [],
|
||||
notification_actions: [],
|
||||
},
|
||||
linked_project_count: 2,
|
||||
};
|
||||
const initial = () => ({
|
||||
project,
|
||||
initialConfiguration: structuredClone(template.configuration_snapshot),
|
||||
copyAreaOptions: areas.map((value) => ({
|
||||
value,
|
||||
label: value,
|
||||
@@ -100,7 +144,8 @@ if (serve) {
|
||||
const url = new URL(request.url, 'http://localhost');
|
||||
let component = url.pathname.endsWith('/edit')
|
||||
? 'Edit'
|
||||
: url.pathname.includes('/templates/create')
|
||||
: url.pathname.includes('/templates/create') ||
|
||||
url.pathname === '/project-templates/create'
|
||||
? 'Create'
|
||||
: url.pathname === '/project-templates'
|
||||
? 'Index'
|
||||
@@ -117,6 +162,9 @@ if (serve) {
|
||||
if (url.searchParams.has('empty')) {
|
||||
data.templates = [];
|
||||
}
|
||||
if (url.pathname === '/project-templates/create') {
|
||||
data.project = null;
|
||||
}
|
||||
let errors = {};
|
||||
if (request.method !== 'GET') {
|
||||
let body = '';
|
||||
@@ -259,12 +307,30 @@ if (serve) {
|
||||
unlinked.match(/<button[^>]*type="submit"[^>]*>/)?.[0] ?? '',
|
||||
/ disabled(?:[ =>])/,
|
||||
);
|
||||
assert.match(
|
||||
await render('Edit', {
|
||||
template: { ...template, source_project: null },
|
||||
}),
|
||||
/This template cannot be refreshed/,
|
||||
const independent = await render('Edit', {
|
||||
template: {
|
||||
...template,
|
||||
source_project: null,
|
||||
linked_project_count: 0,
|
||||
},
|
||||
});
|
||||
assert.match(independent, /Template configuration/);
|
||||
assert.doesNotMatch(independent, /This template cannot be refreshed/);
|
||||
assert.doesNotMatch(
|
||||
independent.match(/<button[^>]*type="submit"[^>]*>/)?.[0] ?? '',
|
||||
/ disabled(?:[ =>])/,
|
||||
);
|
||||
const empty = await render('Create', {
|
||||
project: null,
|
||||
template: undefined,
|
||||
});
|
||||
assert.match(empty, /New empty template/);
|
||||
assert.match(
|
||||
empty.match(/<input[^>]*id="name"[^>]*>/)?.[0] ?? '',
|
||||
/\bvalue(?:="")?(?:\s|>)/,
|
||||
);
|
||||
assert.match(empty, /Template configuration/);
|
||||
assert.match(list, /href="\/project-templates\/create"/);
|
||||
assert.match(
|
||||
await render(
|
||||
'Index',
|
||||
|
||||
Reference in New Issue
Block a user