blob: 7abe762a413991815e529ed25b766fe61df5e3b3 [file] [log] [blame]
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../../test/common-test-setup';
import './gr-repo-labels';
import {GrRepoLabels} from './gr-repo-labels';
import {
queryAndAssert,
stubRestApi,
waitEventLoop,
} from '../../../test/test-utils';
import {RepoName} from '../../../types/common';
import {assert, fixture, html} from '@open-wc/testing';
import {GrButton} from '../../shared/gr-button/gr-button';
import {
LabelDefinitionInfo,
LabelDefinitionInfoFunction,
} from '../../../api/rest-api';
import {GrDropdownList} from '../../shared/gr-dropdown-list/gr-dropdown-list';
import {GrListView} from '../../shared/gr-list-view/gr-list-view';
suite('gr-repo-labels tests', () => {
let element: GrRepoLabels;
setup(async () => {
element = await fixture(html`<gr-repo-labels></gr-repo-labels>`);
});
suite('labels table', () => {
setup(() => {
stubRestApi('getRepoLabels').returns(
Promise.resolve([
{
name: 'Code-Review',
description: 'This is a cool label',
function: 'MaxWithBlock',
values: undefined,
default_value: 0,
copy_condition: 'is:MIN',
unset_copy_condition: true,
allow_post_submit: true,
can_override: true,
ignore_self_approval: false,
branches: ['refs/heads/main'],
},
] as unknown as LabelDefinitionInfo[])
);
element.repo = 'test' as RepoName;
});
test('render loading', () => {
element.repo = 'test2' as RepoName;
assert.shadowDom.equal(
element,
/* HTML */ `<gr-list-view>
<table class="genericList" id="list">
<tbody>
<tr class="headerRow">
<th class="topHeader">Name</th>
<th class="topHeader">Description</th>
<th class="topHeader">Function</th>
<th class="topHeader">Default Value</th>
<th class="topHeader">Copy Condition</th>
<th class="topHeader">Allow Post Submit</th>
<th class="topHeader">Can Override</th>
<th class="topHeader">Ignore Self Approval</th>
<th class="topHeader">Branches</th>
<th class="topHeader">Values</th>
</tr>
</tbody>
<tbody id="labels">
<tr id="loadingContainer">
<td>Loading...</td>
</tr>
</tbody>
</table>
</gr-list-view>`
);
});
test('render', async () => {
await waitEventLoop();
assert.shadowDom.equal(
element,
/* HTML */ `
<gr-list-view>
<table class="genericList" id="list">
<tbody>
<tr class="headerRow">
<th class="topHeader">Name</th>
<th class="topHeader">Description</th>
<th class="topHeader">Function</th>
<th class="topHeader">Default Value</th>
<th class="topHeader">Copy Condition</th>
<th class="topHeader">Allow Post Submit</th>
<th class="topHeader">Can Override</th>
<th class="topHeader">Ignore Self Approval</th>
<th class="topHeader">Branches</th>
<th class="topHeader">Values</th>
</tr>
</tbody>
<tbody id="labels">
<tr class="table">
<td class="name">Code-Review</td>
<td class="description">This is a cool label</td>
<td class="function">
MaxWithBlock
<gr-icon
icon="warning"
title="This function is deprecated."
></gr-icon>
</td>
<td class="defaultValue">0</td>
<td class="copyCondition">is:MIN</td>
<td class="allowPostSubmit">✓</td>
<td class="canOverride">✓</td>
<td class="ignoreSelfApproval"></td>
<td class="branches">refs/heads/main</td>
<td class="values"></td>
</tr>
</tbody>
</table>
</gr-list-view>
`
);
});
test('render as admin', async () => {
await waitEventLoop();
element.isProjectOwner = true;
await element.updateComplete;
assert.shadowDom.equal(
element,
`
<gr-list-view>
<table class="genericList" id="list">
<tbody>
<tr class="headerRow">
<th class="topHeader">Name</th>
<th class="topHeader">Description</th>
<th class="topHeader">Function</th>
<th class="topHeader">Default Value</th>
<th class="topHeader">Copy Condition</th>
<th class="topHeader">Allow Post Submit</th>
<th class="topHeader">Can Override</th>
<th class="topHeader">Ignore Self Approval</th>
<th class="topHeader">Branches</th>
<th class="topHeader">Values</th>
<th class="topHeader"></th>
</tr>
</tbody>
<tbody id="labels">
<tr class="table">
<td class="name">Code-Review</td>
<td class="description">This is a cool label</td>
<td class="function">
MaxWithBlock
<gr-icon
icon="warning"
title="This function is deprecated."
></gr-icon>
</td>
<td class="defaultValue">0</td>
<td class="copyCondition">is:MIN</td>
<td class="allowPostSubmit">✓</td>
<td class="canOverride">✓</td>
<td class="ignoreSelfApproval"></td>
<td class="branches">refs/heads/main</td>
<td class="values"></td>
<td class="actions">
<gr-button
aria-disabled="false"
class="editBtn"
link=""
role="button"
tabindex="0"
>
Edit
</gr-button>
<gr-button
aria-disabled="false"
class="deleteBtn"
link=""
role="button"
tabindex="0"
>
Delete
</gr-button>
</td>
</tr>
</tbody>
</table>
</gr-list-view>
<dialog id="createDialog" tabindex="-1">
<gr-dialog>
<div class="header" slot="header">Create Label</div>
<div class="main" slot="main">
<div class="gr-form-styles">
<div id="form">
<section>
<div class="title-flex">
<span class="title"> Name </span>
</div>
<div class="value-flex">
<span class="value">
<md-outlined-text-field
autocomplete=""
class="showBlueFocusBorder"
id="name"
inputmode=""
required=""
type="text"
>
</md-outlined-text-field>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title">Description</span>
</div>
<div class="value-flex">
<span class="value">
<md-outlined-text-field
autocomplete=""
class="showBlueFocusBorder"
id="description"
inputmode=""
type="text"
>
</md-outlined-text-field>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title"> Function </span>
</div>
<div class="value-flex">
<span class="value">
<gr-dropdown-list> </gr-dropdown-list>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title"> Default Value </span>
</div>
<div class="value-flex">
<span class="value">
<md-outlined-text-field
autocomplete=""
class="showBlueFocusBorder"
id="defaultValue"
inputmode=""
type="number"
>
</md-outlined-text-field>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title"> Copy Condition </span>
</div>
<div class="value-flex">
<span class="value">
<md-outlined-text-field
autocomplete=""
class="showBlueFocusBorder"
id="copyCondition"
inputmode=""
type="text"
>
</md-outlined-text-field>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title">Can Override</span>
</div>
<div class="value-flex">
<span class="value">
<input id="canOverride" checked="" type="checkbox" />
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title">Allow Post Submit</span>
</div>
<div class="value-flex">
<span class="value">
<input id="allowPostSubmit" type="checkbox" />
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title">Ignore Self Approval</span>
</div>
<div class="value-flex">
<span class="value">
<input id="ignoreSelfApproval" type="checkbox" />
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title">Branches</span>
</div>
<div class="value-flex">
<span class="value">
<textarea id="branches" rows="2"></textarea>
</span>
</div>
</section>
<section>
<div class="title-flex">
<span class="title"> Values </span>
</div>
<div class="value-flex">
<span class="value">
<textarea id="values"></textarea>
</span>
</div>
</section>
</div>
</div>
</div>
<div class="footer" slot="footer">
<gr-button aria-disabled="false" role="button" link=""
tabindex="0"
>Cancel</gr-button
>
<gr-button
aria-disabled="true"
class="action save-button"
disabled=""
link=""
primary=""
role="button"
tabindex="-1"
>Create</gr-button
>
<gr-button
aria-disabled="true"
class="action save-for-review"
disabled=""
link=""
primary=""
role="button"
tabindex="-1"
>Save for review</gr-button
>
</div>
</gr-dialog>
</dialog>
<dialog id="deleteDialog" tabindex="-1">
<gr-dialog>
<div class="header" slot="header">Delete Label</div>
<div class="main" slot="main">
Are you sure you want to delete the label
""?
</div>
<div
class="footer"
slot="footer"
>
<gr-button
aria-disabled="false"
role="button"
link=""
tabindex="0"
>
Cancel
</gr-button>
<gr-button
aria-disabled="false"
class="action"
link=""
role="button"
tabindex="0"
>
Delete
</gr-button>
<gr-button
aria-disabled="false"
class="action"
primary=""
link=""
role="button"
tabindex="0"
>
Delete for review
</gr-button>
</div>
</gr-dialog>
</dialog>
`
);
});
test('open edit dialog', async () => {
await waitEventLoop();
element.isProjectOwner = true;
await element.updateComplete;
const editButton = queryAndAssert<GrButton>(element, '.editBtn');
editButton.click();
await element.updateComplete;
// Verify dialog is open and has correct title
const dialog = queryAndAssert<HTMLDialogElement>(
element,
'#createDialog'
);
assert.isTrue(dialog.open);
const dialogTitle = queryAndAssert<HTMLElement>(element, '.header');
assert.equal(dialogTitle.textContent?.trim(), 'Edit Label');
// Verify form is populated with correct data
const nameInput = queryAndAssert<HTMLInputElement>(element, '#name');
assert.equal(nameInput.value, 'Code-Review');
assert.isTrue(nameInput.disabled);
const descriptionInput = queryAndAssert<HTMLInputElement>(
element,
'#description'
);
assert.equal(descriptionInput.value, 'This is a cool label');
const functionDropdown = queryAndAssert<GrDropdownList>(
element,
'gr-dropdown-list'
);
assert.equal(functionDropdown.value, 'MaxWithBlock');
assert.deepEqual(
functionDropdown.items,
Object.values(LabelDefinitionInfoFunction).map(fun => {
return {
text: fun,
value: fun,
};
})
);
const copyConditionInput = queryAndAssert<HTMLInputElement>(
element,
'#copyCondition'
);
assert.equal(copyConditionInput.value, 'is:MIN');
const canOverrideInput = queryAndAssert<HTMLInputElement>(
element,
'#canOverride'
);
assert.isTrue(canOverrideInput.checked);
const allowPostSubmitInput = queryAndAssert<HTMLInputElement>(
element,
'#allowPostSubmit'
);
assert.isTrue(allowPostSubmitInput.checked);
const ignoreSelfApprovalInput = queryAndAssert<HTMLInputElement>(
element,
'#ignoreSelfApproval'
);
assert.isFalse(ignoreSelfApprovalInput.checked);
const branchesTextarea = queryAndAssert<HTMLTextAreaElement>(
element,
'#branches'
);
assert.equal(branchesTextarea.value, 'refs/heads/main');
// Verify save button is enabled
const saveButton = queryAndAssert<HTMLElement>(element, 'gr-dialog');
assert.isFalse(saveButton.hasAttribute('disabled'));
});
test('open create dialog', async () => {
await waitEventLoop();
element.isProjectOwner = true;
await element.updateComplete;
const listView = queryAndAssert<GrListView>(element, 'gr-list-view');
listView.dispatchEvent(
new CustomEvent('create-clicked', {
bubbles: true,
composed: true,
})
);
await element.updateComplete;
// Verify dialog is open and has correct title
const dialog = queryAndAssert<HTMLDialogElement>(
element,
'#createDialog'
);
assert.isTrue(dialog.open);
const dialogTitle = queryAndAssert<HTMLElement>(element, '.header');
assert.equal(dialogTitle.textContent?.trim(), 'Create Label');
const copyConditionInput = queryAndAssert<HTMLInputElement>(
element,
'#copyCondition'
);
assert.equal(copyConditionInput.value, '');
});
test('unset_copy_condition is set when copy_condition is cleared', async () => {
const updateLabelStub = stubRestApi('updateRepoLabel').returns(
Promise.resolve(undefined)
);
await waitEventLoop();
element.isProjectOwner = true;
await element.updateComplete;
const editButton = queryAndAssert<GrButton>(element, '.editBtn');
editButton.click();
await element.updateComplete;
const copyConditionInput = queryAndAssert<HTMLInputElement>(
element,
'#copyCondition'
);
copyConditionInput.value = '';
copyConditionInput.dispatchEvent(
new CustomEvent('input', {
bubbles: true,
composed: true,
})
);
await element.updateComplete;
const saveButton = queryAndAssert<GrButton>(
element,
'gr-dialog gr-button.action.save-button'
);
saveButton.click();
await element.updateComplete;
assert.isTrue(updateLabelStub.calledOnce);
assert.isTrue(updateLabelStub.firstCall.args[2].unset_copy_condition);
});
});
suite('admin', () => {
setup(async () => {
element.isProjectOwner = true;
await element.updateComplete;
});
test('save button is disabled when require_change_for_config_update is set', async () => {
element.disableSaveWithoutReview = true;
element.newLabel.name = 'Test';
await element.updateComplete;
const dialog = queryAndAssert<HTMLDialogElement>(
element,
'#createDialog'
);
dialog.showModal();
await element.updateComplete;
const saveButton = queryAndAssert<GrButton>(
element,
'gr-dialog gr-button.action.save-button'
);
assert.isTrue(saveButton.disabled);
});
});
});