Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/elements/issue-detail/mr-launch-overview/mr-phase.test.js b/static_src/elements/issue-detail/mr-launch-overview/mr-phase.test.js
new file mode 100644
index 0000000..d55897e
--- /dev/null
+++ b/static_src/elements/issue-detail/mr-launch-overview/mr-phase.test.js
@@ -0,0 +1,209 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chai';
+import sinon from 'sinon';
+
+import {MrPhase} from './mr-phase.js';
+
+
+let element;
+
+describe('mr-phase', () => {
+ beforeEach(() => {
+ element = document.createElement('mr-phase');
+ document.body.appendChild(element);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(element);
+ });
+
+ it('initializes', () => {
+ assert.instanceOf(element, MrPhase);
+ });
+
+ it('clicking edit button opens edit dialog', async () => {
+ element.phaseName = 'Beta';
+
+ await element.updateComplete;
+
+ const editDialog = element.querySelector('#editPhase');
+ assert.isFalse(editDialog.opened);
+
+ element.querySelector('.phase-edit').click();
+
+ await element.updateComplete;
+
+ assert.isTrue(editDialog.opened);
+ });
+
+ it('discarding form changes closes dialog', async () => {
+ await element.updateComplete;
+
+ // Open the edit dialog.
+ element.edit();
+ const editDialog = element.querySelector('#editPhase');
+ const editForm = element.querySelector('#metadataForm');
+
+ await element.updateComplete;
+
+ assert.isTrue(editDialog.opened);
+ editForm.discard();
+
+ await element.updateComplete;
+
+ assert.isFalse(editDialog.opened);
+ });
+
+ describe('milestone fetching', () => {
+ beforeEach(() => {
+ sinon.stub(element, 'fetchMilestoneData');
+ });
+
+ it('_launchedMilestone extracts M-Launched for phase', () => {
+ element._fieldValueMap = new Map([['m-launched beta', ['87']]]);
+ element.phaseName = 'Beta';
+
+ assert.equal(element._launchedMilestone, '87');
+ assert.equal(element._approvedMilestone, undefined);
+ assert.equal(element._targetMilestone, undefined);
+ });
+
+ it('_approvedMilestone extracts M-Approved for phase', () => {
+ element._fieldValueMap = new Map([['m-approved beta', ['86']]]);
+ element.phaseName = 'Beta';
+
+ assert.equal(element._launchedMilestone, undefined);
+ assert.equal(element._approvedMilestone, '86');
+ assert.equal(element._targetMilestone, undefined);
+ });
+
+ it('_targetMilestone extracts M-Target for phase', () => {
+ element._fieldValueMap = new Map([['m-target beta', ['85']]]);
+ element.phaseName = 'Beta';
+
+ assert.equal(element._launchedMilestone, undefined);
+ assert.equal(element._approvedMilestone, undefined);
+ assert.equal(element._targetMilestone, '85');
+ });
+
+ it('_milestoneToFetch returns empty when no relevant milestone', () => {
+ element._fieldValueMap = new Map([['m-target beta', ['85']]]);
+ element.phaseName = 'Stable';
+
+ assert.equal(element._milestoneToFetch, '');
+ });
+
+ it('_milestoneToFetch selects highest milestone', () => {
+ element._fieldValueMap = new Map([
+ ['m-target beta', ['84']],
+ ['m-approved beta', ['85']],
+ ['m-launched beta', ['86']]]);
+ element.phaseName = 'Beta';
+
+ assert.equal(element._milestoneToFetch, '86');
+ });
+
+ it('does not fetch when no milestones specified', async () => {
+ element.issue = {projectName: 'chromium', localId: 12};
+
+ await element.updateComplete;
+
+ sinon.assert.notCalled(element.fetchMilestoneData);
+ });
+
+ it('does not fetch when milestone to fetch is unchanged', async () => {
+ element._fetchedMilestone = '86';
+ element._fieldValueMap = new Map([['m-target beta', ['86']]]);
+ element.phaseName = 'Beta';
+
+ await element.updateComplete;
+
+ sinon.assert.notCalled(element.fetchMilestoneData);
+ });
+
+ it('fetches when milestone found', async () => {
+ element._fetchedMilestone = undefined;
+ element._fieldValueMap = new Map([['m-target beta', ['86']]]);
+ element.phaseName = 'Beta';
+
+ await element.updateComplete;
+
+ sinon.assert.calledWith(element.fetchMilestoneData, '86');
+ });
+
+ it('re-fetches when new milestone found', async () => {
+ element._fetchedMilestone = '86';
+ element._fieldValueMap = new Map([
+ ['m-target beta', ['86']],
+ ['m-launched beta', ['87']]]);
+ element.phaseName = 'Beta';
+
+ await element.updateComplete;
+
+ sinon.assert.calledWith(element.fetchMilestoneData, '87');
+ });
+
+ it('re-fetches only after last stale fetch finishes', async () => {
+ element._fetchedMilestone = '84';
+ element._fieldValueMap = new Map([['m-target beta', ['86']]]);
+ element.phaseName = 'Beta';
+ element._isFetchingMilestone = true;
+
+ await element.updateComplete;
+
+ sinon.assert.notCalled(element.fetchMilestoneData);
+
+ // Previous in flight fetch finishes.
+ element._fetchedMilestone = '85';
+ element._isFetchingMilestone = false;
+
+ await element.updateComplete;
+
+ sinon.assert.calledWith(element.fetchMilestoneData, '86');
+ });
+ });
+
+ describe('milestone fetching with fake server responses', () => {
+ beforeEach(() => {
+ sinon.stub(window, 'fetch');
+ sinon.spy(element, 'fetchMilestoneData');
+ });
+
+ afterEach(() => {
+ window.fetch.restore();
+ });
+
+ it('does not refetch when server response finishes', async () => {
+ const response = new window.Response('{"mstones": [{"mstone": 86}]}', {
+ status: 200,
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+
+ window.fetch.returns(Promise.resolve(response));
+
+ element._fieldValueMap = new Map([['m-target beta', ['86']]]);
+ element.phaseName = 'Beta';
+
+ await element.updateComplete;
+
+ sinon.assert.calledWith(element.fetchMilestoneData, '86');
+
+ assert.isTrue(element._isFetchingMilestone);
+
+ await element._fetchMilestoneComplete;
+
+ assert.deepEqual(element._milestoneData, {'mstones': [{'mstone': 86}]});
+ assert.equal(element._fetchedMilestone, '86');
+ assert.isFalse(element._isFetchingMilestone);
+
+ await element.updateComplete;
+
+ sinon.assert.calledOnce(element.fetchMilestoneData);
+ });
+ });
+});