Merge changes from topic "gr-change-view_tests-to-ts"
* changes: Convert gr-change-view tests to typescript Rename gr-change-view_test.js to gr-change-view_test.ts Remove generateChange method and instead use test-data-generators.ts Add test-data-generators and convert several test files to TS Cleanup eslint rules and fix some eslint warnings Allow to write and run Typescript tests
This commit is contained in:
@@ -280,28 +280,17 @@ module.exports = {
|
|||||||
// it catches almost all errors related to invalid usage of this.
|
// it catches almost all errors related to invalid usage of this.
|
||||||
"no-invalid-this": "off",
|
"no-invalid-this": "off",
|
||||||
|
|
||||||
|
"node/no-extraneous-import": "off",
|
||||||
|
|
||||||
|
// Typescript already checks for undef
|
||||||
|
"no-undef": "off",
|
||||||
|
|
||||||
"jsdoc/no-types": 2,
|
"jsdoc/no-types": 2,
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"project": path.resolve(__dirname, "./tsconfig_eslint.json"),
|
"project": path.resolve(__dirname, "./tsconfig_eslint.json"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"files": ["**/*.ts"],
|
|
||||||
"excludedFiles": "*.d.ts",
|
|
||||||
"rules": {
|
|
||||||
// Custom rule from the //tools/js/eslint-rules directory.
|
|
||||||
// See //tools/js/eslint-rules/README.md for details
|
|
||||||
"ts-imports-js": 2,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["**/*.d.ts"],
|
|
||||||
"rules": {
|
|
||||||
// See details in the //tools/js/eslint-rules/report-ts-error.js file.
|
|
||||||
"report-ts-error": "error",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files": ["*.html", "test.js", "test-infra.js"],
|
"files": ["*.html", "test.js", "test-infra.js"],
|
||||||
"rules": {
|
"rules": {
|
||||||
@@ -311,8 +300,6 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"*.html",
|
"*.html",
|
||||||
"common-test-setup.js",
|
|
||||||
"common-test-setup-karma.js",
|
|
||||||
"*_test.js",
|
"*_test.js",
|
||||||
"a11y-test-utils.js",
|
"a11y-test-utils.js",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ load("//tools/js:eslint.bzl", "eslint")
|
|||||||
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
# This list must be in sync with the "include" list in the tsconfig.json file
|
# This list must be in sync with the "include" list in the follwoing files:
|
||||||
|
# tsconfig.json, tsconfig_bazel.json, tsconfig_bazel_test.json
|
||||||
src_dirs = [
|
src_dirs = [
|
||||||
"constants",
|
"constants",
|
||||||
"elements",
|
"elements",
|
||||||
@@ -27,6 +28,7 @@ compiled_pg_srcs = compile_ts(
|
|||||||
]],
|
]],
|
||||||
exclude = [
|
exclude = [
|
||||||
"**/*_test.js",
|
"**/*_test.js",
|
||||||
|
"**/*_test.ts",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
# The same outdir also appears in the following files:
|
# The same outdir also appears in the following files:
|
||||||
@@ -40,6 +42,7 @@ compiled_pg_srcs_with_tests = compile_ts(
|
|||||||
[
|
[
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
|
"test/@types/*.d.ts",
|
||||||
],
|
],
|
||||||
exclude = [
|
exclude = [
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
@@ -48,6 +51,7 @@ compiled_pg_srcs_with_tests = compile_ts(
|
|||||||
"rollup.config.js",
|
"rollup.config.js",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
include_tests = True,
|
||||||
# The same outdir also appears in the following files:
|
# The same outdir also appears in the following files:
|
||||||
# wct_test.sh
|
# wct_test.sh
|
||||||
# karma.conf.js
|
# karma.conf.js
|
||||||
|
|||||||
@@ -369,3 +369,34 @@ export enum AuthType {
|
|||||||
CUSTOM_EXTENSION = 'CUSTOM_EXTENSION',
|
CUSTOM_EXTENSION = 'CUSTOM_EXTENSION',
|
||||||
DEVELOPMENT_BECOME_ANY_ACCOUNT = 'DEVELOPMENT_BECOME_ANY_ACCOUNT',
|
DEVELOPMENT_BECOME_ANY_ACCOUNT = 'DEVELOPMENT_BECOME_ANY_ACCOUNT',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls visibility of other users' dashboard pages and completion suggestions to web users
|
||||||
|
* https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#accounts.visibility
|
||||||
|
*/
|
||||||
|
export enum AccountsVisibility {
|
||||||
|
ALL = 'ALL',
|
||||||
|
SAME_GROUP = 'SAME_GROUP',
|
||||||
|
VISIBLE_GROUP = 'VISIBLE_GROUP',
|
||||||
|
NONE = 'NONE',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account fields that are editable
|
||||||
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#auth-info
|
||||||
|
*/
|
||||||
|
export enum EditableAccountField {
|
||||||
|
FULL_NAME = 'FULL_NAME',
|
||||||
|
USER_NAME = 'USER_NAME',
|
||||||
|
REGISTER_NEW_EMAIL = 'REGISTER_NEW_EMAIL',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting determines when Gerrit computes if a change is mergeable or not.
|
||||||
|
* https://gerrit-review.googlesource.com/Documentation/config-gerrit.html#change.mergeabilityComputationBehavior
|
||||||
|
*/
|
||||||
|
export enum MergeabilityComputationBehavior {
|
||||||
|
API_REF_UPDATED_AND_CHANGE_REINDEX = 'API_REF_UPDATED_AND_CHANGE_REINDEX',
|
||||||
|
REF_UPDATED_AND_CHANGE_REINDEX = 'REF_UPDATED_AND_CHANGE_REINDEX',
|
||||||
|
NEVER = 'NEVER',
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ import {
|
|||||||
import {InheritedBooleanInfoConfiguredValue} from '../../../constants/constants';
|
import {InheritedBooleanInfoConfiguredValue} from '../../../constants/constants';
|
||||||
import {hasOwnProperty} from '../../../utils/common-util';
|
import {hasOwnProperty} from '../../../utils/common-util';
|
||||||
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
||||||
|
import {GrAutocomplete} from '../../shared/gr-autocomplete/gr-autocomplete';
|
||||||
|
import {IronAutogrowTextareaElement} from '@polymer/iron-autogrow-textarea/iron-autogrow-textarea';
|
||||||
|
|
||||||
const SUGGESTIONS_LIMIT = 15;
|
const SUGGESTIONS_LIMIT = 15;
|
||||||
const REF_PREFIX = 'refs/heads/';
|
const REF_PREFIX = 'refs/heads/';
|
||||||
@@ -45,6 +47,9 @@ export interface GrCreateChangeDialog {
|
|||||||
$: {
|
$: {
|
||||||
restAPI: RestApiService & Element;
|
restAPI: RestApiService & Element;
|
||||||
privateChangeCheckBox: HTMLInputElement;
|
privateChangeCheckBox: HTMLInputElement;
|
||||||
|
branchInput: GrAutocomplete;
|
||||||
|
tagNameInput: HTMLInputElement;
|
||||||
|
messageInput: IronAutogrowTextareaElement;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@customElement('gr-create-change-dialog')
|
@customElement('gr-create-change-dialog')
|
||||||
|
|||||||
@@ -17,40 +17,48 @@
|
|||||||
|
|
||||||
import '../../../test/common-test-setup-karma.js';
|
import '../../../test/common-test-setup-karma.js';
|
||||||
import './gr-create-change-dialog.js';
|
import './gr-create-change-dialog.js';
|
||||||
|
import {GrCreateChangeDialog} from './gr-create-change-dialog';
|
||||||
|
import {BranchName, GitRef, RepoName} from '../../../types/common';
|
||||||
|
import {InheritedBooleanInfoConfiguredValue} from '../../../constants/constants';
|
||||||
|
import {createChange, createConfig} from '../../../test/test-data-generators';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-create-change-dialog');
|
const basicFixture = fixtureFromElement('gr-create-change-dialog');
|
||||||
|
|
||||||
suite('gr-create-change-dialog tests', () => {
|
suite('gr-create-change-dialog tests', () => {
|
||||||
let element;
|
let element: GrCreateChangeDialog;
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
stub('gr-rest-api-interface', {
|
stub('gr-rest-api-interface', {
|
||||||
getLoggedIn() { return Promise.resolve(true); },
|
getLoggedIn() {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
},
|
||||||
getRepoBranches(input) {
|
getRepoBranches(input) {
|
||||||
if (input.startsWith('test')) {
|
if (input.startsWith('test')) {
|
||||||
return Promise.resolve([
|
return Promise.resolve([
|
||||||
{
|
{
|
||||||
ref: 'refs/heads/test-branch',
|
ref: 'refs/heads/test-branch' as GitRef,
|
||||||
revision: '67ebf73496383c6777035e374d2d664009e2aa5c',
|
revision: '67ebf73496383c6777035e374d2d664009e2aa5c',
|
||||||
can_delete: true,
|
can_delete: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({});
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
element = basicFixture.instantiate();
|
element = basicFixture.instantiate();
|
||||||
element.repoName = 'test-repo';
|
element.repoName = 'test-repo' as RepoName;
|
||||||
element._repoConfig = {
|
element._repoConfig = {
|
||||||
|
...createConfig(),
|
||||||
private_by_default: {
|
private_by_default: {
|
||||||
configured_value: 'FALSE',
|
value: false,
|
||||||
|
configured_value: InheritedBooleanInfoConfiguredValue.FALSE,
|
||||||
inherited_value: false,
|
inherited_value: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
test('new change created with default', done => {
|
test('new change created with default', async () => {
|
||||||
const configInputObj = {
|
const configInputObj = {
|
||||||
branch: 'test-branch',
|
branch: 'test-branch',
|
||||||
subject: 'first change created with polygerrit ui',
|
subject: 'first change created with polygerrit ui',
|
||||||
@@ -59,35 +67,32 @@ suite('gr-create-change-dialog tests', () => {
|
|||||||
work_in_progress: true,
|
work_in_progress: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveStub = sinon.stub(element.$.restAPI,
|
const saveStub = sinon
|
||||||
'createChange').callsFake(() => Promise.resolve({}));
|
.stub(element.$.restAPI, 'createChange')
|
||||||
|
.callsFake(() => Promise.resolve(createChange()));
|
||||||
|
|
||||||
element.branch = 'test-branch';
|
element.branch = 'test-branch' as BranchName;
|
||||||
element.topic = 'test-topic';
|
element.topic = 'test-topic';
|
||||||
element.subject = 'first change created with polygerrit ui';
|
element.subject = 'first change created with polygerrit ui';
|
||||||
assert.isFalse(element.$.privateChangeCheckBox.checked);
|
assert.isFalse(element.$.privateChangeCheckBox.checked);
|
||||||
|
|
||||||
element.$.branchInput.bindValue = configInputObj.branch;
|
|
||||||
element.$.tagNameInput.bindValue = configInputObj.topic;
|
|
||||||
element.$.messageInput.bindValue = configInputObj.subject;
|
element.$.messageInput.bindValue = configInputObj.subject;
|
||||||
|
|
||||||
element.handleCreateChange().then(() => {
|
await element.handleCreateChange();
|
||||||
// Private change
|
// Private change
|
||||||
assert.isFalse(saveStub.lastCall.args[4]);
|
assert.isFalse(saveStub.lastCall.args[4]);
|
||||||
// WIP Change
|
// WIP Change
|
||||||
assert.isTrue(saveStub.lastCall.args[5]);
|
assert.isTrue(saveStub.lastCall.args[5]);
|
||||||
assert.isTrue(saveStub.called);
|
assert.isTrue(saveStub.called);
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('new change created with private', done => {
|
test('new change created with private', async () => {
|
||||||
element.privateByDefault = {
|
element.privateByDefault = {
|
||||||
configured_value: 'TRUE',
|
configured_value: InheritedBooleanInfoConfiguredValue.TRUE,
|
||||||
inherited_value: false,
|
inherited_value: false,
|
||||||
|
value: true,
|
||||||
};
|
};
|
||||||
sinon.stub(element, '_formatBooleanString')
|
sinon.stub(element, '_formatBooleanString').callsFake(() => true);
|
||||||
.callsFake(() => Promise.resolve(true));
|
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
const configInputObj = {
|
const configInputObj = {
|
||||||
@@ -98,26 +103,23 @@ suite('gr-create-change-dialog tests', () => {
|
|||||||
work_in_progress: true,
|
work_in_progress: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveStub = sinon.stub(element.$.restAPI,
|
const saveStub = sinon
|
||||||
'createChange').callsFake(() => Promise.resolve({}));
|
.stub(element.$.restAPI, 'createChange')
|
||||||
|
.callsFake(() => Promise.resolve(createChange()));
|
||||||
|
|
||||||
element.branch = 'test-branch';
|
element.branch = 'test-branch' as BranchName;
|
||||||
element.topic = 'test-topic';
|
element.topic = 'test-topic';
|
||||||
element.subject = 'first change created with polygerrit ui';
|
element.subject = 'first change created with polygerrit ui';
|
||||||
assert.isTrue(element.$.privateChangeCheckBox.checked);
|
assert.isTrue(element.$.privateChangeCheckBox.checked);
|
||||||
|
|
||||||
element.$.branchInput.bindValue = configInputObj.branch;
|
|
||||||
element.$.tagNameInput.bindValue = configInputObj.topic;
|
|
||||||
element.$.messageInput.bindValue = configInputObj.subject;
|
element.$.messageInput.bindValue = configInputObj.subject;
|
||||||
|
|
||||||
element.handleCreateChange().then(() => {
|
await element.handleCreateChange();
|
||||||
// Private change
|
// Private change
|
||||||
assert.isTrue(saveStub.lastCall.args[4]);
|
assert.isTrue(saveStub.lastCall.args[4]);
|
||||||
// WIP Change
|
// WIP Change
|
||||||
assert.isTrue(saveStub.lastCall.args[5]);
|
assert.isTrue(saveStub.lastCall.args[5]);
|
||||||
assert.isTrue(saveStub.called);
|
assert.isTrue(saveStub.called);
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('_getRepoBranchesSuggestions empty', done => {
|
test('_getRepoBranchesSuggestions empty', done => {
|
||||||
@@ -145,4 +147,3 @@ suite('gr-create-change-dialog tests', () => {
|
|||||||
assert.equal(element._computePrivateSectionClass(false), '');
|
assert.equal(element._computePrivateSectionClass(false), '');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,7 +20,11 @@ import './gr-change-actions.js';
|
|||||||
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
|
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
|
||||||
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
||||||
import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
|
import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
|
||||||
import {generateChange} from '../../../test/test-utils.js';
|
import {
|
||||||
|
createChange,
|
||||||
|
createChangeMessages,
|
||||||
|
createRevisions,
|
||||||
|
} from '../../../test/test-data-generators.js';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-change-actions');
|
const basicFixture = fixtureFromElement('gr-change-actions');
|
||||||
|
|
||||||
@@ -1810,10 +1814,11 @@ suite('gr-change-actions tests', () => {
|
|||||||
element.changeNum = 42;
|
element.changeNum = 42;
|
||||||
element.change._number = 42;
|
element.change._number = 42;
|
||||||
element.latestPatchNum = 12;
|
element.latestPatchNum = 12;
|
||||||
element.change = generateChange({
|
element.change = {
|
||||||
revisionsCount: element.latestPatchNum,
|
...createChange(),
|
||||||
messagesCount: 1,
|
revisions: createRevisions(element.latestPatchNum),
|
||||||
});
|
messages: createChangeMessages(1),
|
||||||
|
};
|
||||||
payload = {foo: 'bar'};
|
payload = {foo: 'bar'};
|
||||||
|
|
||||||
onShowError = sinon.stub();
|
onShowError = sinon.stub();
|
||||||
@@ -1826,12 +1831,12 @@ suite('gr-change-actions tests', () => {
|
|||||||
let sendStub;
|
let sendStub;
|
||||||
setup(() => {
|
setup(() => {
|
||||||
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
||||||
.returns(Promise.resolve(
|
.returns(Promise.resolve({
|
||||||
generateChange({
|
...createChange(),
|
||||||
// element has latest info
|
// element has latest info
|
||||||
revisionsCount: element.latestPatchNum,
|
revisions: createRevisions(element.latestPatchNum),
|
||||||
messagesCount: 1,
|
messages: createChangeMessages(1),
|
||||||
})));
|
}));
|
||||||
sendStub = sinon.stub(element.$.restAPI, 'executeChangeAction')
|
sendStub = sinon.stub(element.$.restAPI, 'executeChangeAction')
|
||||||
.returns(Promise.resolve({}));
|
.returns(Promise.resolve({}));
|
||||||
getResponseObjectStub = sinon.stub(element.$.restAPI,
|
getResponseObjectStub = sinon.stub(element.$.restAPI,
|
||||||
@@ -1945,12 +1950,12 @@ suite('gr-change-actions tests', () => {
|
|||||||
suite('failure modes', () => {
|
suite('failure modes', () => {
|
||||||
test('non-latest', () => {
|
test('non-latest', () => {
|
||||||
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
||||||
.returns(Promise.resolve(
|
.returns(Promise.resolve({
|
||||||
generateChange({
|
...createChange(),
|
||||||
// new patchset was uploaded
|
// new patchset was uploaded
|
||||||
revisionsCount: element.latestPatchNum + 1,
|
revisions: createRevisions(element.latestPatchNum + 1),
|
||||||
messagesCount: 1,
|
messages: createChangeMessages(1),
|
||||||
})));
|
}));
|
||||||
const sendStub = sinon.stub(element.$.restAPI,
|
const sendStub = sinon.stub(element.$.restAPI,
|
||||||
'executeChangeAction');
|
'executeChangeAction');
|
||||||
|
|
||||||
@@ -1965,12 +1970,12 @@ suite('gr-change-actions tests', () => {
|
|||||||
|
|
||||||
test('send fails', () => {
|
test('send fails', () => {
|
||||||
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
sinon.stub(element.$.restAPI, 'getChangeDetail')
|
||||||
.returns(Promise.resolve(
|
.returns(Promise.resolve({
|
||||||
generateChange({
|
...createChange(),
|
||||||
// element has latest info
|
// element has latest info
|
||||||
revisionsCount: element.latestPatchNum,
|
revisions: createRevisions(element.latestPatchNum),
|
||||||
messagesCount: 1,
|
messages: createChangeMessages(1),
|
||||||
})));
|
}));
|
||||||
const sendStub = sinon.stub(element.$.restAPI,
|
const sendStub = sinon.stub(element.$.restAPI,
|
||||||
'executeChangeAction').callsFake(
|
'executeChangeAction').callsFake(
|
||||||
(num, method, patchNum, endpoint, payload, onErr) => {
|
(num, method, patchNum, endpoint, payload, onErr) => {
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ import {
|
|||||||
} from '../../../constants/constants';
|
} from '../../../constants/constants';
|
||||||
import {changeIsOpen} from '../../../utils/change-util';
|
import {changeIsOpen} from '../../../utils/change-util';
|
||||||
import {customElement, property, observe} from '@polymer/decorators';
|
import {customElement, property, observe} from '@polymer/decorators';
|
||||||
import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
import {
|
||||||
|
EditRevisionInfo,
|
||||||
|
ParsedChangeInfo,
|
||||||
|
} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
||||||
import {
|
import {
|
||||||
AccountDetailInfo,
|
AccountDetailInfo,
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
@@ -135,7 +138,7 @@ export class GrChangeMetadata extends GestureEventListeners(
|
|||||||
account?: AccountDetailInfo;
|
account?: AccountDetailInfo;
|
||||||
|
|
||||||
@property({type: Object})
|
@property({type: Object})
|
||||||
revision?: RevisionInfo;
|
revision?: RevisionInfo | EditRevisionInfo;
|
||||||
|
|
||||||
@property({type: Object})
|
@property({type: Object})
|
||||||
commitInfo?: CommitInfoWithRequiredCommit;
|
commitInfo?: CommitInfoWithRequiredCommit;
|
||||||
@@ -612,7 +615,7 @@ export class GrChangeMetadata extends GestureEventListeners(
|
|||||||
|
|
||||||
_computeParents(
|
_computeParents(
|
||||||
change?: ParsedChangeInfo,
|
change?: ParsedChangeInfo,
|
||||||
revision?: RevisionInfo
|
revision?: RevisionInfo | EditRevisionInfo
|
||||||
): ParentCommitInfo[] {
|
): ParentCommitInfo[] {
|
||||||
if (!revision || !revision.commit) {
|
if (!revision || !revision.commit) {
|
||||||
if (!change || !change.current_revision) {
|
if (!change || !change.current_revision) {
|
||||||
|
|||||||
@@ -132,7 +132,10 @@ import {
|
|||||||
import {AppElementChangeViewParams} from '../../gr-app-types';
|
import {AppElementChangeViewParams} from '../../gr-app-types';
|
||||||
import {DropdownLink} from '../../shared/gr-dropdown/gr-dropdown';
|
import {DropdownLink} from '../../shared/gr-dropdown/gr-dropdown';
|
||||||
import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs';
|
import {PaperTabsElement} from '@polymer/paper-tabs/paper-tabs';
|
||||||
import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
import {
|
||||||
|
EditRevisionInfo,
|
||||||
|
ParsedChangeInfo,
|
||||||
|
} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
||||||
import {
|
import {
|
||||||
GrFileList,
|
GrFileList,
|
||||||
DEFAULT_NUM_FILES_SHOWN,
|
DEFAULT_NUM_FILES_SHOWN,
|
||||||
@@ -142,8 +145,12 @@ import {
|
|||||||
CustomKeyboardEvent,
|
CustomKeyboardEvent,
|
||||||
EditableContentSaveEvent,
|
EditableContentSaveEvent,
|
||||||
OpenFixPreviewEvent,
|
OpenFixPreviewEvent,
|
||||||
|
ShowAlertEventDetail,
|
||||||
SwitchTabEvent,
|
SwitchTabEvent,
|
||||||
} from '../../../types/events';
|
} from '../../../types/events';
|
||||||
|
import {GrButton} from '../../shared/gr-button/gr-button';
|
||||||
|
import {GrMessagesList} from '../gr-messages-list/gr-messages-list';
|
||||||
|
import {GrThreadList} from '../gr-thread-list/gr-thread-list';
|
||||||
|
|
||||||
const CHANGE_ID_ERROR = {
|
const CHANGE_ID_ERROR = {
|
||||||
MISMATCH: 'mismatch',
|
MISMATCH: 'mismatch',
|
||||||
@@ -217,8 +224,15 @@ export interface GrChangeView {
|
|||||||
metadata: GrChangeMetadata;
|
metadata: GrChangeMetadata;
|
||||||
relatedChangesToggle: HTMLDivElement;
|
relatedChangesToggle: HTMLDivElement;
|
||||||
mainChangeInfo: HTMLDivElement;
|
mainChangeInfo: HTMLDivElement;
|
||||||
|
commitCollapseToggleButton: GrButton;
|
||||||
|
commitCollapseToggle: HTMLDivElement;
|
||||||
|
relatedChangesToggleButton: GrButton;
|
||||||
|
replyBtn: GrButton;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChangeViewPatchRange = Partial<PatchRange>;
|
||||||
|
|
||||||
@customElement('gr-change-view')
|
@customElement('gr-change-view')
|
||||||
export class GrChangeView extends KeyboardShortcutMixin(
|
export class GrChangeView extends KeyboardShortcutMixin(
|
||||||
GestureEventListeners(LegacyElementMixin(PolymerElement))
|
GestureEventListeners(LegacyElementMixin(PolymerElement))
|
||||||
@@ -377,7 +391,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
_changeIdCommitMessageError?: string;
|
_changeIdCommitMessageError?: string;
|
||||||
|
|
||||||
@property({type: Object})
|
@property({type: Object})
|
||||||
_patchRange?: PatchRange;
|
_patchRange?: ChangeViewPatchRange;
|
||||||
|
|
||||||
@property({type: String})
|
@property({type: String})
|
||||||
_filesExpanded?: string;
|
_filesExpanded?: string;
|
||||||
@@ -386,7 +400,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
_basePatchNum?: string;
|
_basePatchNum?: string;
|
||||||
|
|
||||||
@property({type: Object})
|
@property({type: Object})
|
||||||
_selectedRevision?: RevisionInfo;
|
_selectedRevision?: RevisionInfo | EditRevisionInfo;
|
||||||
|
|
||||||
@property({type: Object})
|
@property({type: Object})
|
||||||
_currentRevisionActions?: ActionNameToActionInfoMap;
|
_currentRevisionActions?: ActionNameToActionInfoMap;
|
||||||
@@ -431,7 +445,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
type: String,
|
type: String,
|
||||||
computed: '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
|
computed: '_computeChangeStatusChips(_change, _mergeable, _submitEnabled)',
|
||||||
})
|
})
|
||||||
_changeStatuses?: string;
|
_changeStatuses?: string[];
|
||||||
|
|
||||||
/** If false, then the "Show more" button was used to expand. */
|
/** If false, then the "Show more" button was used to expand. */
|
||||||
@property({type: Boolean})
|
@property({type: Boolean})
|
||||||
@@ -653,11 +667,11 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get messagesList() {
|
get messagesList(): GrMessagesList | null {
|
||||||
return this.shadowRoot!.querySelector('gr-messages-list');
|
return this.shadowRoot!.querySelector('gr-messages-list');
|
||||||
}
|
}
|
||||||
|
|
||||||
get threadList() {
|
get threadList(): GrThreadList | null {
|
||||||
return this.shadowRoot!.querySelector('gr-thread-list');
|
return this.shadowRoot!.querySelector('gr-thread-list');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,9 +882,9 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
loggedIn: boolean,
|
loggedIn: boolean,
|
||||||
editing: boolean,
|
editing: boolean,
|
||||||
change: ChangeInfo,
|
change: ChangeInfo,
|
||||||
editMode: boolean,
|
editMode?: boolean,
|
||||||
collapsed: boolean,
|
collapsed?: boolean,
|
||||||
collapsible: boolean
|
collapsible?: boolean
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
!loggedIn ||
|
!loggedIn ||
|
||||||
@@ -1223,14 +1237,13 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
this._patchRange.basePatchNum !== value.basePatchNum);
|
this._patchRange.basePatchNum !== value.basePatchNum);
|
||||||
const changeChanged = this._changeNum !== value.changeNum;
|
const changeChanged = this._changeNum !== value.changeNum;
|
||||||
|
|
||||||
const patchRange = {
|
const patchRange: ChangeViewPatchRange = {
|
||||||
patchNum: value.patchNum,
|
patchNum: value.patchNum,
|
||||||
basePatchNum: value.basePatchNum || 'PARENT',
|
basePatchNum: value.basePatchNum || ParentPatchSetNum,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$.fileList.collapseAllDiffs();
|
this.$.fileList.collapseAllDiffs();
|
||||||
// TODO(TS): change patchRange to PatchRange.
|
this._patchRange = patchRange;
|
||||||
this._patchRange = patchRange as PatchRange;
|
|
||||||
|
|
||||||
// If the change has already been loaded and the parameter change is only
|
// If the change has already been loaded and the parameter change is only
|
||||||
// in the patch range, then don't do a full reload.
|
// in the patch range, then don't do a full reload.
|
||||||
@@ -1475,7 +1488,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
*/
|
*/
|
||||||
_getBasePatchNum(
|
_getBasePatchNum(
|
||||||
change: ChangeInfo | ParsedChangeInfo,
|
change: ChangeInfo | ParsedChangeInfo,
|
||||||
patchRange: PatchRange
|
patchRange: ChangeViewPatchRange
|
||||||
) {
|
) {
|
||||||
if (patchRange.basePatchNum && patchRange.basePatchNum !== 'PARENT') {
|
if (patchRange.basePatchNum && patchRange.basePatchNum !== 'PARENT') {
|
||||||
return patchRange.basePatchNum;
|
return patchRange.basePatchNum;
|
||||||
@@ -1575,7 +1588,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
GrChangeView,
|
GrChangeView,
|
||||||
'_diffDrafts'
|
'_diffDrafts'
|
||||||
> | null,
|
> | null,
|
||||||
canStartReview?: PolymerDeepPropertyChange<boolean, boolean>
|
canStartReview?: boolean
|
||||||
) {
|
) {
|
||||||
if (changeRecord === undefined || canStartReview === undefined) {
|
if (changeRecord === undefined || canStartReview === undefined) {
|
||||||
return 'Reply';
|
return 'Reply';
|
||||||
@@ -1685,11 +1698,12 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
throw new Error('missing required _patchRange property');
|
throw new Error('missing required _patchRange property');
|
||||||
const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
|
const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
|
||||||
if (patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
|
if (patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
|
||||||
|
const detail: ShowAlertEventDetail = {
|
||||||
|
message: 'Latest is already selected.',
|
||||||
|
};
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('show-alert', {
|
new CustomEvent('show-alert', {
|
||||||
detail: {
|
detail,
|
||||||
message: 'Latest is already selected.',
|
|
||||||
},
|
|
||||||
composed: true,
|
composed: true,
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
})
|
})
|
||||||
@@ -1927,7 +1941,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
basePatchNum: edit.base_patch_set_number,
|
basePatchNum: edit.base_patch_set_number,
|
||||||
commit: edit.commit,
|
commit: edit.commit,
|
||||||
fetch: edit.fetch,
|
fetch: edit.fetch,
|
||||||
} as RevisionInfo;
|
};
|
||||||
|
|
||||||
// If the edit is based on the most recent patchset, load it by
|
// If the edit is based on the most recent patchset, load it by
|
||||||
// default, unless another patch set to load was specified in the URL.
|
// default, unless another patch set to load was specified in the URL.
|
||||||
@@ -2568,7 +2582,7 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
this.$.relatedChanges.reload();
|
this.$.relatedChanges.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeHeaderClass(editMode: boolean) {
|
_computeHeaderClass(editMode?: boolean) {
|
||||||
const classes = ['header'];
|
const classes = ['header'];
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
classes.push('editMode');
|
classes.push('editMode');
|
||||||
@@ -2577,7 +2591,10 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
_computeEditMode(
|
_computeEditMode(
|
||||||
patchRangeRecord: PolymerDeepPropertyChange<PatchRange, PatchRange>,
|
patchRangeRecord: PolymerDeepPropertyChange<
|
||||||
|
ChangeViewPatchRange,
|
||||||
|
ChangeViewPatchRange
|
||||||
|
>,
|
||||||
paramsRecord: PolymerDeepPropertyChange<
|
paramsRecord: PolymerDeepPropertyChange<
|
||||||
AppElementChangeViewParams,
|
AppElementChangeViewParams,
|
||||||
AppElementChangeViewParams
|
AppElementChangeViewParams
|
||||||
@@ -2734,7 +2751,10 @@ export class GrChangeView extends KeyboardShortcutMixin(
|
|||||||
* Wrapper for using in the element template and computed properties
|
* Wrapper for using in the element template and computed properties
|
||||||
*/
|
*/
|
||||||
_hasEditPatchsetLoaded(
|
_hasEditPatchsetLoaded(
|
||||||
patchRangeRecord: PolymerDeepPropertyChange<PatchRange, PatchRange>
|
patchRangeRecord: PolymerDeepPropertyChange<
|
||||||
|
ChangeViewPatchRange,
|
||||||
|
ChangeViewPatchRange
|
||||||
|
>
|
||||||
) {
|
) {
|
||||||
const patchRange = patchRangeRecord.base;
|
const patchRange = patchRangeRecord.base;
|
||||||
if (!patchRange) {
|
if (!patchRange) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@ import '../../../test/common-test-setup-karma.js';
|
|||||||
import './gr-file-list-header.js';
|
import './gr-file-list-header.js';
|
||||||
import {FilesExpandedState} from '../gr-file-list-constants.js';
|
import {FilesExpandedState} from '../gr-file-list-constants.js';
|
||||||
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
||||||
import {generateChange} from '../../../test/test-utils.js';
|
|
||||||
import 'lodash/lodash.js';
|
import 'lodash/lodash.js';
|
||||||
|
import {createRevisions} from '../../../test/test-data-generators.js';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-file-list-header');
|
const basicFixture = fixtureFromElement('gr-file-list-header');
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ suite('gr-file-list-header tests', () => {
|
|||||||
|
|
||||||
test('patch specific elements', () => {
|
test('patch specific elements', () => {
|
||||||
element.editMode = true;
|
element.editMode = true;
|
||||||
element.allPatchSets = generateChange({revisionsCount: 2}).revisions;
|
element.allPatchSets = createRevisions(2);
|
||||||
flush();
|
flush();
|
||||||
|
|
||||||
assert.isFalse(isVisible(element.$.diffPrefsContainer));
|
assert.isFalse(isVisible(element.$.diffPrefsContainer));
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
} from '../../shared/gr-autocomplete/gr-autocomplete';
|
} from '../../shared/gr-autocomplete/gr-autocomplete';
|
||||||
import {getDocsBaseUrl} from '../../../utils/url-util';
|
import {getDocsBaseUrl} from '../../../utils/url-util';
|
||||||
import {CustomKeyboardEvent} from '../../../types/events';
|
import {CustomKeyboardEvent} from '../../../types/events';
|
||||||
|
import {MergeabilityComputationBehavior} from '../../../constants/constants';
|
||||||
|
|
||||||
// Possible static search options for auto complete, without negations.
|
// Possible static search options for auto complete, without negations.
|
||||||
const SEARCH_OPERATORS = [
|
const SEARCH_OPERATORS = [
|
||||||
@@ -197,8 +198,10 @@ export class GrSearchBar extends KeyboardShortcutMixin(
|
|||||||
serverConfig.change &&
|
serverConfig.change &&
|
||||||
serverConfig.change.mergeability_computation_behavior;
|
serverConfig.change.mergeability_computation_behavior;
|
||||||
if (
|
if (
|
||||||
mergeability === 'API_REF_UPDATED_AND_CHANGE_REINDEX' ||
|
mergeability ===
|
||||||
mergeability === 'REF_UPDATED_AND_CHANGE_REINDEX'
|
MergeabilityComputationBehavior.API_REF_UPDATED_AND_CHANGE_REINDEX ||
|
||||||
|
mergeability ===
|
||||||
|
MergeabilityComputationBehavior.REF_UPDATED_AND_CHANGE_REINDEX
|
||||||
) {
|
) {
|
||||||
// add 'is:mergeable' to SEARCH_OPERATORS_WITH_NEGATIONS_SET
|
// add 'is:mergeable' to SEARCH_OPERATORS_WITH_NEGATIONS_SET
|
||||||
this._addOperator('is:mergeable');
|
this._addOperator('is:mergeable');
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
|||||||
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
|
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
|
||||||
import {sortComments} from '../../../utils/comment-util.js';
|
import {sortComments} from '../../../utils/comment-util.js';
|
||||||
import {Side} from '../../../constants/constants.js';
|
import {Side} from '../../../constants/constants.js';
|
||||||
import {generateChange} from '../../../test/test-utils';
|
import {createChange} from '../../../test/test-data-generators.js';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-diff-host');
|
const basicFixture = fixtureFromElement('gr-diff-host');
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
});
|
});
|
||||||
test('plugin layers requested', () => {
|
test('plugin layers requested', () => {
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.reload();
|
element.reload();
|
||||||
assert(element.$.jsAPI.getDiffLayers.called);
|
assert(element.$.jsAPI.getDiffLayers.called);
|
||||||
});
|
});
|
||||||
@@ -323,7 +323,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
||||||
Promise.resolve({content: []}));
|
Promise.resolve({content: []}));
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||||||
element.prefs = prefs;
|
element.prefs = prefs;
|
||||||
return element.reload(true);
|
return element.reload(true);
|
||||||
@@ -344,7 +344,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
||||||
Promise.resolve({content: []}));
|
Promise.resolve({content: []}));
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.reload();
|
element.reload();
|
||||||
// Multiple cascading microtasks are scheduled.
|
// Multiple cascading microtasks are scheduled.
|
||||||
await flush();
|
await flush();
|
||||||
@@ -366,7 +366,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
sinon.stub(element.$.restAPI, 'getDiff').returns(
|
||||||
Promise.resolve({content: []}));
|
Promise.resolve({content: []}));
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
let reloadComplete = false;
|
let reloadComplete = false;
|
||||||
element.$.restAPI.getDiffPreferences()
|
element.$.restAPI.getDiffPreferences()
|
||||||
.then(prefs => {
|
.then(prefs => {
|
||||||
@@ -392,7 +392,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
// Stub the network calls into requests that never resolve.
|
// Stub the network calls into requests that never resolve.
|
||||||
sinon.stub(element, '_getDiff').callsFake(() => new Promise(() => {}));
|
sinon.stub(element, '_getDiff').callsFake(() => new Promise(() => {}));
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
|
|
||||||
// Needs to be set to something first for it to cancel.
|
// Needs to be set to something first for it to cancel.
|
||||||
element.diff = {
|
element.diff = {
|
||||||
@@ -410,7 +410,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
getLoggedIn = false;
|
getLoggedIn = false;
|
||||||
element = basicFixture.instantiate();
|
element = basicFixture.instantiate();
|
||||||
element.changeNum = 123;
|
element.changeNum = 123;
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.path = 'some/path';
|
element.path = 'some/path';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
|
element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.comments = {
|
element.comments = {
|
||||||
left: [],
|
left: [],
|
||||||
right: [],
|
right: [],
|
||||||
@@ -1447,7 +1447,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.prefs = prefs;
|
element.prefs = prefs;
|
||||||
element.changeNum = 123;
|
element.changeNum = 123;
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.path = 'some/path';
|
element.path = 'some/path';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1502,7 +1502,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
element.patchRange = {};
|
element.patchRange = {};
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.prefs = prefs;
|
element.prefs = prefs;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ suite('gr-diff-host tests', () => {
|
|||||||
});
|
});
|
||||||
element = basicFixture.instantiate();
|
element = basicFixture.instantiate();
|
||||||
element.changeNum = 123;
|
element.changeNum = 123;
|
||||||
element.change = generateChange();
|
element.change = createChange();
|
||||||
element.path = 'some/path';
|
element.path = 'some/path';
|
||||||
const prefs = {
|
const prefs = {
|
||||||
line_length: 10,
|
line_length: 10,
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ import '../../../test/common-test-setup-karma.js';
|
|||||||
import './gr-diff-view.js';
|
import './gr-diff-view.js';
|
||||||
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
|
||||||
import {ChangeStatus} from '../../../constants/constants.js';
|
import {ChangeStatus} from '../../../constants/constants.js';
|
||||||
import {generateChange, TestKeyboardShortcutBinder} from '../../../test/test-utils';
|
import {TestKeyboardShortcutBinder} from '../../../test/test-utils.js';
|
||||||
import {SPECIAL_PATCH_SET_NUM} from '../../../utils/patch-set-util.js';
|
import {SPECIAL_PATCH_SET_NUM} from '../../../utils/patch-set-util.js';
|
||||||
import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
|
import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
|
||||||
import {_testOnly_findCommentById} from '../gr-comment-api/gr-comment-api.js';
|
import {_testOnly_findCommentById} from '../gr-comment-api/gr-comment-api.js';
|
||||||
import {appContext} from '../../../services/app-context.js';
|
import {appContext} from '../../../services/app-context.js';
|
||||||
import {GerritView} from '../../core/gr-navigation/gr-navigation.js';
|
import {GerritView} from '../../core/gr-navigation/gr-navigation.js';
|
||||||
|
import {
|
||||||
|
createChange,
|
||||||
|
createRevisions,
|
||||||
|
} from '../../../test/test-data-generators.js';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-diff-view');
|
const basicFixture = fixtureFromElement('gr-diff-view');
|
||||||
|
|
||||||
@@ -184,8 +188,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
sinon.stub(element.reporting, 'diffViewDisplayed');
|
sinon.stub(element.reporting, 'diffViewDisplayed');
|
||||||
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
||||||
sinon.spy(element, '_paramsChanged');
|
sinon.spy(element, '_paramsChanged');
|
||||||
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve(
|
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve({
|
||||||
generateChange({revisionsCount: 11})));
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
}));
|
||||||
element.params = {
|
element.params = {
|
||||||
view: GerritNav.View.DIFF,
|
view: GerritNav.View.DIFF,
|
||||||
changeNum: '42',
|
changeNum: '42',
|
||||||
@@ -197,7 +203,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
left: [{id: 'c1', __commentSide: 'left', line: 10}],
|
left: [{id: 'c1', __commentSide: 'left', line: 10}],
|
||||||
right: [{id: 'c2', __commentSide: 'right', line: 11}],
|
right: [{id: 'c2', __commentSide: 'right', line: 11}],
|
||||||
});
|
});
|
||||||
element._change = generateChange({revisionsCount: 11});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
};
|
||||||
return element._paramsChanged.returnValues[0].then(() => {
|
return element._paramsChanged.returnValues[0].then(() => {
|
||||||
assert.isTrue(initLineOfInterestAndCursorStub.
|
assert.isTrue(initLineOfInterestAndCursorStub.
|
||||||
calledWithExactly(true));
|
calledWithExactly(true));
|
||||||
@@ -237,8 +246,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
||||||
sinon.stub(element, '_isFileUnchanged').returns(true);
|
sinon.stub(element, '_isFileUnchanged').returns(true);
|
||||||
sinon.spy(element, '_paramsChanged');
|
sinon.spy(element, '_paramsChanged');
|
||||||
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve(
|
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve({
|
||||||
generateChange({revisionsCount: 11})));
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
}));
|
||||||
element.params = {
|
element.params = {
|
||||||
view: GerritNav.View.DIFF,
|
view: GerritNav.View.DIFF,
|
||||||
changeNum: '42',
|
changeNum: '42',
|
||||||
@@ -247,7 +258,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
commentId: 'c1',
|
commentId: 'c1',
|
||||||
};
|
};
|
||||||
sinon.stub(element.$.diffHost, '_commentsChanged');
|
sinon.stub(element.$.diffHost, '_commentsChanged');
|
||||||
element._change = generateChange({revisionsCount: 11});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
};
|
||||||
return element._paramsChanged.returnValues[0].then(() => {
|
return element._paramsChanged.returnValues[0].then(() => {
|
||||||
assert.isTrue(diffNavStub.lastCall.calledWithExactly(
|
assert.isTrue(diffNavStub.lastCall.calledWithExactly(
|
||||||
element._change, '/COMMIT_MSG', 2, 'PARENT', 10));
|
element._change, '/COMMIT_MSG', 2, 'PARENT', 10));
|
||||||
@@ -262,8 +276,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
sinon.stub(element.$.diffHost, 'reload').returns(Promise.resolve());
|
||||||
sinon.stub(element, '_isFileUnchanged').returns(true);
|
sinon.stub(element, '_isFileUnchanged').returns(true);
|
||||||
sinon.spy(element, '_paramsChanged');
|
sinon.spy(element, '_paramsChanged');
|
||||||
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve(
|
sinon.stub(element, '_getChangeDetail').returns(Promise.resolve({
|
||||||
generateChange({revisionsCount: 11})));
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
}));
|
||||||
element.params = {
|
element.params = {
|
||||||
view: GerritNav.View.DIFF,
|
view: GerritNav.View.DIFF,
|
||||||
changeNum: '42',
|
changeNum: '42',
|
||||||
@@ -272,7 +288,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
commentId: 'c3',
|
commentId: 'c3',
|
||||||
};
|
};
|
||||||
sinon.stub(element.$.diffHost, '_commentsChanged');
|
sinon.stub(element.$.diffHost, '_commentsChanged');
|
||||||
element._change = generateChange({revisionsCount: 11});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
};
|
||||||
return element._paramsChanged.returnValues[0].then(() => {
|
return element._paramsChanged.returnValues[0].then(() => {
|
||||||
assert.isFalse(diffNavStub.called);
|
assert.isFalse(diffNavStub.called);
|
||||||
});
|
});
|
||||||
@@ -317,7 +336,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
element.$.restAPI.getDiffChangeDetail.restore();
|
element.$.restAPI.getDiffChangeDetail.restore();
|
||||||
sinon.stub(element.$.restAPI, 'getDiffChangeDetail')
|
sinon.stub(element.$.restAPI, 'getDiffChangeDetail')
|
||||||
.returns(
|
.returns(
|
||||||
Promise.resolve(generateChange({revisionsCount: 11})));
|
Promise.resolve({
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(11),
|
||||||
|
}));
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
patchNum: 2,
|
patchNum: 2,
|
||||||
basePatchNum: 1,
|
basePatchNum: 1,
|
||||||
@@ -472,7 +494,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('diff against latest', () => {
|
test('diff against latest', () => {
|
||||||
element._change = generateChange({revisionsCount: 12});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(12),
|
||||||
|
};
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
basePatchNum: 5,
|
basePatchNum: 5,
|
||||||
patchNum: 10,
|
patchNum: 10,
|
||||||
@@ -486,7 +511,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('_handleDiffBaseAgainstLeft', () => {
|
test('_handleDiffBaseAgainstLeft', () => {
|
||||||
element._change = generateChange({revisionsCount: 10});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(10),
|
||||||
|
};
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
patchNum: 3,
|
patchNum: 3,
|
||||||
basePatchNum: 1,
|
basePatchNum: 1,
|
||||||
@@ -504,7 +532,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
|
|
||||||
test('_handleDiffBaseAgainstLeft when initially navigating to a comment',
|
test('_handleDiffBaseAgainstLeft when initially navigating to a comment',
|
||||||
() => {
|
() => {
|
||||||
element._change = generateChange({revisionsCount: 10});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(10),
|
||||||
|
};
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
patchNum: 3,
|
patchNum: 3,
|
||||||
basePatchNum: 1,
|
basePatchNum: 1,
|
||||||
@@ -523,7 +554,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('_handleDiffRightAgainstLatest', () => {
|
test('_handleDiffRightAgainstLatest', () => {
|
||||||
element._change = generateChange({revisionsCount: 10});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(10),
|
||||||
|
};
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
basePatchNum: 1,
|
basePatchNum: 1,
|
||||||
patchNum: 3,
|
patchNum: 3,
|
||||||
@@ -538,7 +572,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('_handleDiffBaseAgainstLatest', () => {
|
test('_handleDiffBaseAgainstLatest', () => {
|
||||||
element._change = generateChange({revisionsCount: 10});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(10),
|
||||||
|
};
|
||||||
element._patchRange = {
|
element._patchRange = {
|
||||||
basePatchNum: 1,
|
basePatchNum: 1,
|
||||||
patchNum: 3,
|
patchNum: 3,
|
||||||
@@ -1404,9 +1441,6 @@ suite('gr-diff-view tests', () => {
|
|||||||
|
|
||||||
suite('_initPatchRange', () => {
|
suite('_initPatchRange', () => {
|
||||||
setup(async () => {
|
setup(async () => {
|
||||||
// const changeDetail = generateChange({revisionsCount: 5});
|
|
||||||
// sinon.stub(element.$.restAPI, 'getDiffChangeDetail')
|
|
||||||
// .returns(Promise.resolve(changeDetail));
|
|
||||||
element.params = {
|
element.params = {
|
||||||
view: GerritView.DIFF,
|
view: GerritView.DIFF,
|
||||||
changeNum: '42',
|
changeNum: '42',
|
||||||
@@ -1701,7 +1735,10 @@ suite('gr-diff-view tests', () => {
|
|||||||
patchNum: 1,
|
patchNum: 1,
|
||||||
basePatchNum: 'PARENT',
|
basePatchNum: 'PARENT',
|
||||||
};
|
};
|
||||||
element._change = generateChange({revisionsCount: 1});
|
element._change = {
|
||||||
|
...createChange(),
|
||||||
|
revisions: createRevisions(1),
|
||||||
|
};
|
||||||
flush();
|
flush();
|
||||||
assert.isTrue(GerritNav.navigateToDiff.notCalled);
|
assert.isTrue(GerritNav.navigateToDiff.notCalled);
|
||||||
|
|
||||||
|
|||||||
@@ -93,4 +93,9 @@ export interface PluginApi {
|
|||||||
attributeHelper(element: Element): GrAttributeHelper;
|
attributeHelper(element: Element): GrAttributeHelper;
|
||||||
restApi(): GrPluginRestApi;
|
restApi(): GrPluginRestApi;
|
||||||
eventHelper(element: Node): GrEventHelper;
|
eventHelper(element: Node): GrEventHelper;
|
||||||
|
registerDynamicCustomComponent(
|
||||||
|
endpointName: string,
|
||||||
|
moduleName?: string,
|
||||||
|
options?: RegisterOptions
|
||||||
|
): HookApi;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {GrPopupInterface} from './gr-popup-interface.js';
|
|||||||
import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
|
import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit.js';
|
||||||
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
|
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
|
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
|
||||||
import {createIronOverlayBackdropStyleEl} from '../../../test/test-utils';
|
import {createIronOverlayBackdropStyleEl} from '../../../test/test-utils.js';
|
||||||
|
|
||||||
class GrUserTestPopupElement extends PolymerElement {
|
class GrUserTestPopupElement extends PolymerElement {
|
||||||
static get is() { return 'gr-user-test-popup'; }
|
static get is() { return 'gr-user-test-popup'; }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {htmlTemplate} from './gr-account-info_html';
|
|||||||
import {customElement, property, observe} from '@polymer/decorators';
|
import {customElement, property, observe} from '@polymer/decorators';
|
||||||
import {AccountInfo, ServerInfo} from '../../../types/common';
|
import {AccountInfo, ServerInfo} from '../../../types/common';
|
||||||
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
||||||
|
import {EditableAccountField} from '../../../constants/constants';
|
||||||
|
|
||||||
export interface GrAccountInfo {
|
export interface GrAccountInfo {
|
||||||
$: {
|
$: {
|
||||||
@@ -211,12 +212,16 @@ export class GrAccountInfo extends GestureEventListeners(
|
|||||||
|
|
||||||
// Username may not be changed once it is set.
|
// Username may not be changed once it is set.
|
||||||
return (
|
return (
|
||||||
config.auth.editable_account_fields.includes('USER_NAME') && !username
|
config.auth.editable_account_fields.includes(
|
||||||
|
EditableAccountField.USER_NAME
|
||||||
|
) && !username
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeNameMutable(config: ServerInfo) {
|
_computeNameMutable(config: ServerInfo) {
|
||||||
return config.auth.editable_account_fields.includes('FULL_NAME');
|
return config.auth.editable_account_fields.includes(
|
||||||
|
EditableAccountField.FULL_NAME
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@observe('_account.status')
|
@observe('_account.status')
|
||||||
|
|||||||
@@ -17,13 +17,14 @@
|
|||||||
|
|
||||||
import '../../../test/common-test-setup-karma.js';
|
import '../../../test/common-test-setup-karma.js';
|
||||||
import './gr-email-editor.js';
|
import './gr-email-editor.js';
|
||||||
|
import {GrEmailEditor} from './gr-email-editor';
|
||||||
|
|
||||||
const basicFixture = fixtureFromElement('gr-email-editor');
|
const basicFixture = fixtureFromElement('gr-email-editor');
|
||||||
|
|
||||||
suite('gr-email-editor tests', () => {
|
suite('gr-email-editor tests', () => {
|
||||||
let element;
|
let element: GrEmailEditor;
|
||||||
|
|
||||||
setup(done => {
|
setup(async () => {
|
||||||
const emails = [
|
const emails = [
|
||||||
{email: 'email@one.com'},
|
{email: 'email@one.com'},
|
||||||
{email: 'email@two.com', preferred: true},
|
{email: 'email@two.com', preferred: true},
|
||||||
@@ -31,36 +32,47 @@ suite('gr-email-editor tests', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
stub('gr-rest-api-interface', {
|
stub('gr-rest-api-interface', {
|
||||||
getAccountEmails() { return Promise.resolve(emails); },
|
getAccountEmails() {
|
||||||
|
return Promise.resolve(emails);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
element = basicFixture.instantiate();
|
element = basicFixture.instantiate();
|
||||||
|
|
||||||
element.loadData().then(flush(done));
|
await element.loadData();
|
||||||
|
await flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders', () => {
|
test('renders', () => {
|
||||||
const rows = element.shadowRoot
|
const rows = element
|
||||||
.querySelector('table').querySelectorAll('tbody tr');
|
.shadowRoot!.querySelector('table')!
|
||||||
|
.querySelectorAll('tbody tr') as NodeListOf<HTMLTableRowElement>;
|
||||||
|
|
||||||
assert.equal(rows.length, 3);
|
assert.equal(rows.length, 3);
|
||||||
|
|
||||||
assert.isFalse(rows[0].querySelector('input[type=radio]').checked);
|
assert.isFalse(
|
||||||
assert.isNotOk(rows[0].querySelector('gr-button').disabled);
|
(rows[0].querySelector('input[type=radio]') as HTMLInputElement).checked
|
||||||
|
);
|
||||||
|
assert.isNotOk(rows[0].querySelector('gr-button')!.disabled);
|
||||||
|
|
||||||
assert.isTrue(rows[1].querySelector('input[type=radio]').checked);
|
assert.isTrue(
|
||||||
assert.isOk(rows[1].querySelector('gr-button').disabled);
|
(rows[1].querySelector('input[type=radio]') as HTMLInputElement).checked
|
||||||
|
);
|
||||||
|
assert.isOk(rows[1].querySelector('gr-button')!.disabled);
|
||||||
|
|
||||||
assert.isFalse(rows[2].querySelector('input[type=radio]').checked);
|
assert.isFalse(
|
||||||
assert.isNotOk(rows[2].querySelector('gr-button').disabled);
|
(rows[2].querySelector('input[type=radio]') as HTMLInputElement).checked
|
||||||
|
);
|
||||||
|
assert.isNotOk(rows[2].querySelector('gr-button')!.disabled);
|
||||||
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('edit preferred', () => {
|
test('edit preferred', () => {
|
||||||
const preferredChangedSpy = sinon.spy(element, '_handlePreferredChange');
|
const preferredChangedSpy = sinon.spy(element, '_handlePreferredChange');
|
||||||
const radios = element.shadowRoot
|
const radios = element
|
||||||
.querySelector('table').querySelectorAll('input[type=radio]');
|
.shadowRoot!.querySelector('table')!
|
||||||
|
.querySelectorAll('input[type=radio]') as NodeListOf<HTMLInputElement>;
|
||||||
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
assert.isNotOk(element._newPreferred);
|
assert.isNotOk(element._newPreferred);
|
||||||
@@ -82,8 +94,9 @@ suite('gr-email-editor tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('delete email', () => {
|
test('delete email', () => {
|
||||||
const buttons = element.shadowRoot
|
const buttons = element
|
||||||
.querySelector('table').querySelectorAll('gr-button');
|
.shadowRoot!.querySelector('table')!
|
||||||
|
.querySelectorAll('gr-button');
|
||||||
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
assert.isNotOk(element._newPreferred);
|
assert.isNotOk(element._newPreferred);
|
||||||
@@ -101,12 +114,15 @@ suite('gr-email-editor tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('save changes', done => {
|
test('save changes', done => {
|
||||||
const deleteEmailStub =
|
const deleteEmailStub = sinon.stub(element.$.restAPI, 'deleteAccountEmail');
|
||||||
sinon.stub(element.$.restAPI, 'deleteAccountEmail');
|
const setPreferredStub = sinon.stub(
|
||||||
const setPreferredStub = sinon.stub(element.$.restAPI,
|
element.$.restAPI,
|
||||||
'setPreferredAccountEmail');
|
'setPreferredAccountEmail'
|
||||||
const rows = element.shadowRoot
|
);
|
||||||
.querySelector('table').querySelectorAll('tbody tr');
|
|
||||||
|
const rows = element
|
||||||
|
.shadowRoot!.querySelector('table')!
|
||||||
|
.querySelectorAll('tbody tr');
|
||||||
|
|
||||||
assert.isFalse(element.hasUnsavedChanges);
|
assert.isFalse(element.hasUnsavedChanges);
|
||||||
assert.isNotOk(element._newPreferred);
|
assert.isNotOk(element._newPreferred);
|
||||||
@@ -114,8 +130,8 @@ suite('gr-email-editor tests', () => {
|
|||||||
assert.equal(element._emails.length, 3);
|
assert.equal(element._emails.length, 3);
|
||||||
|
|
||||||
// Delete the first email and set the last as preferred.
|
// Delete the first email and set the last as preferred.
|
||||||
rows[0].querySelector('gr-button').click();
|
rows[0].querySelector('gr-button')!.click();
|
||||||
rows[2].querySelector('input[type=radio]').click();
|
(rows[2].querySelector('input[type=radio]')! as HTMLInputElement).click();
|
||||||
|
|
||||||
assert.isTrue(element.hasUnsavedChanges);
|
assert.isTrue(element.hasUnsavedChanges);
|
||||||
assert.equal(element._newPreferred, 'email@three.com');
|
assert.equal(element._newPreferred, 'email@three.com');
|
||||||
@@ -135,4 +151,3 @@ suite('gr-email-editor tests', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ import {htmlTemplate} from './gr-registration-dialog_html';
|
|||||||
import {customElement, property, observe} from '@polymer/decorators';
|
import {customElement, property, observe} from '@polymer/decorators';
|
||||||
import {ServerInfo, AccountDetailInfo} from '../../../types/common';
|
import {ServerInfo, AccountDetailInfo} from '../../../types/common';
|
||||||
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
|
||||||
|
import {EditableAccountField} from '../../../constants/constants';
|
||||||
|
|
||||||
export interface GrRegistrationDialog {
|
export interface GrRegistrationDialog {
|
||||||
$: {
|
$: {
|
||||||
@@ -97,7 +98,9 @@ export class GrRegistrationDialog extends GestureEventListeners(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
config.auth.editable_account_fields.includes('USER_NAME') && !username
|
config.auth.editable_account_fields.includes(
|
||||||
|
EditableAccountField.USER_NAME
|
||||||
|
) && !username
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -351,9 +351,7 @@ export class GrRestApiInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fetchSharedCacheURL(
|
_fetchSharedCacheURL(req: FetchJSONRequest): Promise<ParsedJSON | undefined> {
|
||||||
req: FetchJSONRequest
|
|
||||||
): Promise<ParsedJSON | undefined> {
|
|
||||||
// Cache is shared across instances
|
// Cache is shared across instances
|
||||||
return this._restApiHelper.fetchCacheURL(req);
|
return this._restApiHelper.fetchCacheURL(req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ import {
|
|||||||
ChangeInfo,
|
ChangeInfo,
|
||||||
ChangeMessageInfo,
|
ChangeMessageInfo,
|
||||||
ChangeViewChangeInfo,
|
ChangeViewChangeInfo,
|
||||||
|
CommitInfo,
|
||||||
|
PatchSetNum,
|
||||||
ReviewerUpdateInfo,
|
ReviewerUpdateInfo,
|
||||||
|
RevisionInfo,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
} from '../../../types/common';
|
} from '../../../types/common';
|
||||||
import {hasOwnProperty} from '../../../utils/common-util';
|
import {hasOwnProperty} from '../../../utils/common-util';
|
||||||
@@ -78,8 +81,16 @@ interface UpdateItem {
|
|||||||
prev_state?: ReviewerState;
|
prev_state?: ReviewerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EditRevisionInfo extends Partial<RevisionInfo> {
|
||||||
|
// EditRevisionInfo has less required properties then RevisionInfo
|
||||||
|
_number: PatchSetNum;
|
||||||
|
basePatchNum: PatchSetNum;
|
||||||
|
commit: CommitInfo;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ParsedChangeInfo
|
export interface ParsedChangeInfo
|
||||||
extends Omit<ChangeViewChangeInfo, 'reviewer_updates'> {
|
extends Omit<ChangeViewChangeInfo, 'reviewer_updates' | 'revisions'> {
|
||||||
|
revisions: {[revisionId: string]: RevisionInfo | EditRevisionInfo};
|
||||||
reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[];
|
reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -551,6 +551,14 @@ export class ShortcutManager {
|
|||||||
|
|
||||||
private readonly bindings = new Map<Shortcut, string[]>();
|
private readonly bindings = new Map<Shortcut, string[]>();
|
||||||
|
|
||||||
|
public _testOnly_getBindings() {
|
||||||
|
return this.bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _testOnly_isEmpty() {
|
||||||
|
return this.activeHosts.size === 0 && this.listeners.size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly listeners = new Set<ShortcutListener>();
|
private readonly listeners = new Set<ShortcutListener>();
|
||||||
|
|
||||||
bindShortcut(shortcut: Shortcut, ...bindings: string[]) {
|
bindShortcut(shortcut: Shortcut, ...bindings: string[]) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def _get_ts_output_files(outdir, srcs):
|
|||||||
result.append(_get_ts_compiled_path(outdir, f))
|
result.append(_get_ts_compiled_path(outdir, f))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def compile_ts(name, srcs, ts_outdir):
|
def compile_ts(name, srcs, ts_outdir, include_tests = False):
|
||||||
"""Compiles srcs files with the typescript compiler
|
"""Compiles srcs files with the typescript compiler
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -50,16 +50,31 @@ def compile_ts(name, srcs, ts_outdir):
|
|||||||
# List of files produced by the typescript compiler
|
# List of files produced by the typescript compiler
|
||||||
generated_js = _get_ts_output_files(ts_outdir, srcs)
|
generated_js = _get_ts_output_files(ts_outdir, srcs)
|
||||||
|
|
||||||
|
all_srcs = srcs + [
|
||||||
|
":tsconfig.json",
|
||||||
|
":tsconfig_bazel.json",
|
||||||
|
"@ui_npm//:node_modules",
|
||||||
|
]
|
||||||
|
ts_project = "tsconfig_bazel.json"
|
||||||
|
|
||||||
|
if include_tests:
|
||||||
|
all_srcs = all_srcs + [
|
||||||
|
":tsconfig_bazel_test.json",
|
||||||
|
"@ui_dev_npm//:node_modules",
|
||||||
|
]
|
||||||
|
ts_project = "tsconfig_bazel_test.json"
|
||||||
|
|
||||||
# Run the compiler
|
# Run the compiler
|
||||||
native.genrule(
|
native.genrule(
|
||||||
name = ts_rule_name,
|
name = ts_rule_name,
|
||||||
srcs = srcs + [
|
srcs = all_srcs,
|
||||||
":tsconfig.json",
|
|
||||||
"@ui_npm//:node_modules",
|
|
||||||
],
|
|
||||||
outs = generated_js,
|
outs = generated_js,
|
||||||
cmd = " && ".join([
|
cmd = " && ".join([
|
||||||
"$(location //tools/node_tools:tsc-bin) --project $(location :tsconfig.json) --outdir $(RULEDIR)/" + ts_outdir + " --baseUrl ./external/ui_npm/node_modules",
|
"$(location //tools/node_tools:tsc-bin) --project $(location :" +
|
||||||
|
ts_project +
|
||||||
|
") --outdir $(RULEDIR)/" +
|
||||||
|
ts_outdir +
|
||||||
|
" --baseUrl ./external/ui_npm/node_modules/",
|
||||||
]),
|
]),
|
||||||
tools = ["//tools/node_tools:tsc-bin"],
|
tools = ["//tools/node_tools:tsc-bin"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -57,10 +57,16 @@ declare global {
|
|||||||
|
|
||||||
const security = window.security;
|
const security = window.security;
|
||||||
|
|
||||||
export function installPolymerResin(safeTypesBridge: SafeTypeBridge) {
|
export const _testOnly_defaultResinReportHandler =
|
||||||
|
security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER;
|
||||||
|
|
||||||
|
export function installPolymerResin(
|
||||||
|
safeTypesBridge: SafeTypeBridge,
|
||||||
|
reportHandler = security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER
|
||||||
|
) {
|
||||||
window.security.polymer_resin.install({
|
window.security.polymer_resin.install({
|
||||||
allowedIdentifierPrefixes: [''],
|
allowedIdentifierPrefixes: [''],
|
||||||
reportHandler: security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER,
|
reportHandler,
|
||||||
safeTypesBridge,
|
safeTypesBridge,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ export function initErrorReporter(appContext: AppContext) {
|
|||||||
oldOnError(msg, url, line, column, error);
|
oldOnError(msg, url, line, column, error);
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
line = line || (error as any).lineNumber;
|
line = line || error.lineNumber;
|
||||||
column = column || (error as any).columnNumber;
|
column = column || error.columnNumber;
|
||||||
let shortenedErrorStack = msg;
|
let shortenedErrorStack = msg;
|
||||||
if (error.stack) {
|
if (error.stack) {
|
||||||
const errorStackLines = error.stack.split('\n');
|
const errorStackLines = error.stack.split('\n');
|
||||||
|
|||||||
@@ -14,7 +14,23 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
export const grReportingMock = {
|
import {ReportingService, Timer} from './gr-reporting';
|
||||||
|
|
||||||
|
export class MockTimer implements Timer {
|
||||||
|
end(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
withMaximum(_: number): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const grReportingMock: ReportingService = {
|
||||||
appStarted: () => {},
|
appStarted: () => {},
|
||||||
beforeLocationChanged: () => {},
|
beforeLocationChanged: () => {},
|
||||||
changeDisplayed: () => {},
|
changeDisplayed: () => {},
|
||||||
@@ -25,7 +41,7 @@ export const grReportingMock = {
|
|||||||
diffViewFullyLoaded: () => {},
|
diffViewFullyLoaded: () => {},
|
||||||
fileListDisplayed: () => {},
|
fileListDisplayed: () => {},
|
||||||
getTimer: () => {
|
getTimer: () => {
|
||||||
return {end: () => {}};
|
return new MockTimer();
|
||||||
},
|
},
|
||||||
locationChanged: () => {},
|
locationChanged: () => {},
|
||||||
onVisibilityChange: () => {},
|
onVisibilityChange: () => {},
|
||||||
|
|||||||
27
polygerrit-ui/app/test/@types/sinon-esm.d.ts
vendored
Normal file
27
polygerrit-ui/app/test/@types/sinon-esm.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module 'sinon/pkg/sinon-esm' {
|
||||||
|
// sinon-esm doesn't have it's own d.ts, reexport all types from sinon
|
||||||
|
// This is a trick - @types/sinon adds interfaces and sinon instance
|
||||||
|
// to a global variables/namespace. We reexport it here, so we
|
||||||
|
// can use in our code when importing sinon-esm
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default sinon;
|
||||||
|
const sinon: Sinon.SinonStatic;
|
||||||
|
export {SinonSpy, SinonFakeTimers, SinonStubbedMember};
|
||||||
|
}
|
||||||
@@ -14,14 +14,23 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import './common-test-setup.js';
|
import './common-test-setup';
|
||||||
import '@polymer/test-fixture/test-fixture.js';
|
import '@polymer/test-fixture/test-fixture';
|
||||||
import 'chai/chai.js';
|
import 'chai/chai';
|
||||||
self.assert = window.chai.assert;
|
|
||||||
self.expect = window.chai.expect;
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
flush: typeof flushImpl;
|
||||||
|
fixtureFromTemplate: typeof fixtureFromTemplateImpl;
|
||||||
|
fixtureFromElement: typeof fixtureFromElementImpl;
|
||||||
|
}
|
||||||
|
let flush: typeof flushImpl;
|
||||||
|
let fixtureFromTemplate: typeof fixtureFromTemplateImpl;
|
||||||
|
let fixtureFromElement: typeof fixtureFromElementImpl;
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for https://github.com/karma-runner/karma-mocha/issues/227
|
// Workaround for https://github.com/karma-runner/karma-mocha/issues/227
|
||||||
let unhandledError = null;
|
let unhandledError: ErrorEvent;
|
||||||
|
|
||||||
window.addEventListener('error', e => {
|
window.addEventListener('error', e => {
|
||||||
// For uncaught error mochajs doesn't print the full stack trace.
|
// For uncaught error mochajs doesn't print the full stack trace.
|
||||||
@@ -31,7 +40,7 @@ window.addEventListener('error', e => {
|
|||||||
unhandledError = e;
|
unhandledError = e;
|
||||||
});
|
});
|
||||||
|
|
||||||
let originalOnBeforeUnload;
|
let originalOnBeforeUnload: typeof window.onbeforeunload;
|
||||||
|
|
||||||
suiteSetup(() => {
|
suiteSetup(() => {
|
||||||
// This suiteSetup() method is called only once before all tests
|
// This suiteSetup() method is called only once before all tests
|
||||||
@@ -39,7 +48,7 @@ suiteSetup(() => {
|
|||||||
// Can't use window.addEventListener("beforeunload",...) here,
|
// Can't use window.addEventListener("beforeunload",...) here,
|
||||||
// the handler is raised too late.
|
// the handler is raised too late.
|
||||||
originalOnBeforeUnload = window.onbeforeunload;
|
originalOnBeforeUnload = window.onbeforeunload;
|
||||||
window.onbeforeunload = e => {
|
window.onbeforeunload = function (e: BeforeUnloadEvent) {
|
||||||
// If a test reloads a page, we can't prevent it.
|
// If a test reloads a page, we can't prevent it.
|
||||||
// However we can print earror and the stack trace with assert.fail
|
// However we can print earror and the stack trace with assert.fail
|
||||||
try {
|
try {
|
||||||
@@ -48,7 +57,9 @@ suiteSetup(() => {
|
|||||||
console.error('Page reloading attempt detected.');
|
console.error('Page reloading attempt detected.');
|
||||||
console.error(e.stack.toString());
|
console.error(e.stack.toString());
|
||||||
}
|
}
|
||||||
originalOnBeforeUnload(e);
|
if (originalOnBeforeUnload) {
|
||||||
|
originalOnBeforeUnload.call(this, e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,18 +75,21 @@ suiteTeardown(() => {
|
|||||||
// Keep the original one for use in test utils methods.
|
// Keep the original one for use in test utils methods.
|
||||||
const nativeSetTimeout = window.setTimeout;
|
const nativeSetTimeout = window.setTimeout;
|
||||||
|
|
||||||
|
function flushImpl(): Promise<void>;
|
||||||
|
function flushImpl(callback: () => void): void;
|
||||||
/**
|
/**
|
||||||
* Triggers a flush of any pending events, observations, etc and calls you back
|
* Triggers a flush of any pending events, observations, etc and calls you back
|
||||||
* after they have been processed if callback is passed; otherwise returns
|
* after they have been processed if callback is passed; otherwise returns
|
||||||
* promise.
|
* promise.
|
||||||
*
|
|
||||||
* @param {function()} callback
|
|
||||||
*/
|
*/
|
||||||
function flush(callback) {
|
function flushImpl(callback?: () => void): Promise<void> | void {
|
||||||
// Ideally, this function would be a call to Polymer.dom.flush, but that
|
// Ideally, this function would be a call to Polymer.dom.flush, but that
|
||||||
// doesn't support a callback yet
|
// doesn't support a callback yet
|
||||||
// (https://github.com/Polymer/polymer-dev/issues/851)
|
// (https://github.com/Polymer/polymer-dev/issues/851)
|
||||||
window.Polymer.dom.flush();
|
// The type is used only in one place, disable eslint warning instead of
|
||||||
|
// creating an interface
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(window as any).Polymer.dom.flush();
|
||||||
if (callback) {
|
if (callback) {
|
||||||
nativeSetTimeout(callback, 0);
|
nativeSetTimeout(callback, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -85,19 +99,12 @@ function flush(callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flush = flush;
|
self.flush = flushImpl;
|
||||||
|
|
||||||
class TestFixtureIdProvider {
|
class TestFixtureIdProvider {
|
||||||
static get instance() {
|
public static readonly instance: TestFixtureIdProvider = new TestFixtureIdProvider();
|
||||||
if (!TestFixtureIdProvider._instance) {
|
|
||||||
TestFixtureIdProvider._instance = new TestFixtureIdProvider();
|
|
||||||
}
|
|
||||||
return TestFixtureIdProvider._instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
private fixturesCount = 1;
|
||||||
this.fixturesCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
generateNewFixtureId() {
|
generateNewFixtureId() {
|
||||||
this.fixturesCount++;
|
this.fixturesCount++;
|
||||||
@@ -105,22 +112,24 @@ class TestFixtureIdProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TagTestFixture<T extends Element> {
|
||||||
|
instantiate(model?: unknown): T;
|
||||||
|
}
|
||||||
|
|
||||||
class TestFixture {
|
class TestFixture {
|
||||||
constructor(fixtureId) {
|
constructor(private readonly fixtureId: string) {}
|
||||||
this.fixtureId = fixtureId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of a fixture's template.
|
* Create an instance of a fixture's template.
|
||||||
*
|
*
|
||||||
* @param {Object} model - see Data-bound sections at
|
* @param model - see Data-bound sections at
|
||||||
* https://www.webcomponents.org/element/@polymer/test-fixture
|
* https://www.webcomponents.org/element/@polymer/test-fixture
|
||||||
* @return {HTMLElement | HTMLElement[]} - if the fixture's template contains
|
* @return - if the fixture's template contains
|
||||||
* a single element, returns the appropriated instantiated element.
|
* a single element, returns the appropriated instantiated element.
|
||||||
* Otherwise, it return an array of all instantiated elements from the
|
* Otherwise, it return an array of all instantiated elements from the
|
||||||
* template.
|
* template.
|
||||||
*/
|
*/
|
||||||
instantiate(model) {
|
instantiate(model?: unknown): HTMLElement | HTMLElement[] {
|
||||||
// The window.fixture method is defined in common-test-setup.js
|
// The window.fixture method is defined in common-test-setup.js
|
||||||
return window.fixture(this.fixtureId, model);
|
return window.fixture(this.fixtureId, model);
|
||||||
}
|
}
|
||||||
@@ -153,10 +162,9 @@ class TestFixture {
|
|||||||
* });
|
* });
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @param {HTMLTemplateElement} template - a template for a fixture
|
* @param template - a template for a fixture
|
||||||
* @return {TestFixture} - the instance of TestFixture class
|
|
||||||
*/
|
*/
|
||||||
function fixtureFromTemplate(template) {
|
function fixtureFromTemplateImpl(template: HTMLTemplateElement): TestFixture {
|
||||||
const fixtureId = TestFixtureIdProvider.instance.generateNewFixtureId();
|
const fixtureId = TestFixtureIdProvider.instance.generateNewFixtureId();
|
||||||
const testFixture = document.createElement('test-fixture');
|
const testFixture = document.createElement('test-fixture');
|
||||||
testFixture.setAttribute('id', fixtureId);
|
testFixture.setAttribute('id', fixtureId);
|
||||||
@@ -183,14 +191,17 @@ function fixtureFromTemplate(template) {
|
|||||||
* });
|
* });
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @param {HTMLTemplateElement} template - a template for a fixture
|
* @param tagName - a template for a fixture is <tagName></tagName>
|
||||||
* @return {TestFixture} - the instance of TestFixture class
|
|
||||||
*/
|
*/
|
||||||
function fixtureFromElement(tagName) {
|
function fixtureFromElementImpl<T extends keyof HTMLElementTagNameMap>(
|
||||||
|
tagName: T
|
||||||
|
): TagTestFixture<HTMLElementTagNameMap[T]> {
|
||||||
const template = document.createElement('template');
|
const template = document.createElement('template');
|
||||||
template.innerHTML = `<${tagName}></${tagName}>`;
|
template.innerHTML = `<${tagName}></${tagName}>`;
|
||||||
return fixtureFromTemplate(template);
|
return (fixtureFromTemplate(template) as unknown) as TagTestFixture<
|
||||||
|
HTMLElementTagNameMap[T]
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.fixtureFromTemplate = fixtureFromTemplate;
|
window.fixtureFromTemplate = fixtureFromTemplateImpl;
|
||||||
window.fixtureFromElement = fixtureFromElement;
|
window.fixtureFromElement = fixtureFromElementImpl;
|
||||||
@@ -15,51 +15,81 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
// This should be the first import to install handler before any other code
|
// This should be the first import to install handler before any other code
|
||||||
import './source-map-support-install.js';
|
import './source-map-support-install';
|
||||||
// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
|
// TODO(dmfilippov): remove bundled-polymer.js imports when the following issue
|
||||||
// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
|
// https://github.com/Polymer/polymer-resin/issues/9 is resolved.
|
||||||
import '../scripts/bundled-polymer.js';
|
import '../scripts/bundled-polymer';
|
||||||
import 'polymer-resin/standalone/polymer-resin.js';
|
import '@polymer/iron-test-helpers/iron-test-helpers';
|
||||||
import '@polymer/iron-test-helpers/iron-test-helpers.js';
|
import './test-router';
|
||||||
import './test-router.js';
|
|
||||||
import {_testOnlyInitAppContext} from './test-app-context-init';
|
import {_testOnlyInitAppContext} from './test-app-context-init';
|
||||||
import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader.js';
|
import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader';
|
||||||
import {_testOnlyResetRestApi} from '../elements/shared/gr-js-api-interface/gr-plugin-rest-api.js';
|
import {_testOnlyResetRestApi} from '../elements/shared/gr-js-api-interface/gr-plugin-rest-api';
|
||||||
import {_testOnlyResetGrRestApiSharedObjects} from '../elements/shared/gr-rest-api-interface/gr-rest-api-interface.js';
|
import {_testOnlyResetGrRestApiSharedObjects} from '../elements/shared/gr-rest-api-interface/gr-rest-api-interface';
|
||||||
import {cleanupTestUtils, TestKeyboardShortcutBinder} from './test-utils.js';
|
import {
|
||||||
|
cleanupTestUtils,
|
||||||
|
getCleanupsCount,
|
||||||
|
registerTestCleanup,
|
||||||
|
TestKeyboardShortcutBinder,
|
||||||
|
} from './test-utils';
|
||||||
import {flushDebouncers} from '@polymer/polymer/lib/utils/debounce';
|
import {flushDebouncers} from '@polymer/polymer/lib/utils/debounce';
|
||||||
import {_testOnly_getShortcutManagerInstance} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
|
import {_testOnly_getShortcutManagerInstance} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
|
||||||
import sinon from 'sinon/pkg/sinon-esm.js';
|
import sinon, {SinonSpy} from 'sinon/pkg/sinon-esm';
|
||||||
import {safeTypesBridge} from '../utils/safe-types-util.js';
|
import {safeTypesBridge} from '../utils/safe-types-util';
|
||||||
import {_testOnly_initGerritPluginApi} from '../elements/shared/gr-js-api-interface/gr-gerrit.js';
|
import {_testOnly_initGerritPluginApi} from '../elements/shared/gr-js-api-interface/gr-gerrit';
|
||||||
import {initGlobalVariables} from '../elements/gr-app-global-var-init.js';
|
import {initGlobalVariables} from '../elements/gr-app-global-var-init';
|
||||||
|
import 'chai/chai';
|
||||||
|
import {
|
||||||
|
_testOnly_defaultResinReportHandler,
|
||||||
|
installPolymerResin,
|
||||||
|
} from '../scripts/polymer-resin-install';
|
||||||
|
import {hasOwnProperty} from '../utils/common-util';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
assert: typeof chai.assert;
|
||||||
|
expect: typeof chai.expect;
|
||||||
|
fixture: typeof fixtureImpl;
|
||||||
|
stub: typeof stubImpl;
|
||||||
|
sinon: typeof sinon;
|
||||||
|
}
|
||||||
|
let assert: typeof chai.assert;
|
||||||
|
let expect: typeof chai.expect;
|
||||||
|
let stub: typeof stubImpl;
|
||||||
|
let sinon: typeof sinon;
|
||||||
|
}
|
||||||
|
window.assert = chai.assert;
|
||||||
|
window.expect = chai.expect;
|
||||||
|
|
||||||
window.sinon = sinon;
|
window.sinon = sinon;
|
||||||
|
|
||||||
security.polymer_resin.install({
|
installPolymerResin(safeTypesBridge, (isViolation, fmt, ...args) => {
|
||||||
allowedIdentifierPrefixes: [''],
|
const log = _testOnly_defaultResinReportHandler;
|
||||||
reportHandler(isViolation, fmt, ...args) {
|
log(isViolation, fmt, ...args);
|
||||||
const log = security.polymer_resin.CONSOLE_LOGGING_REPORT_HANDLER;
|
if (isViolation) {
|
||||||
log(isViolation, fmt, ...args);
|
// This will cause the test to fail if there is a data binding
|
||||||
if (isViolation) {
|
// violation.
|
||||||
// This will cause the test to fail if there is a data binding
|
throw new Error('polymer-resin violation: ' + fmt + JSON.stringify(args));
|
||||||
// violation.
|
}
|
||||||
throw new Error(
|
|
||||||
'polymer-resin violation: ' + fmt +
|
|
||||||
JSON.stringify(args));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
safeTypesBridge,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cleanups = [];
|
interface TestFixtureElement extends HTMLElement {
|
||||||
|
restore(): void;
|
||||||
|
create(model?: unknown): HTMLElement | HTMLElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFixtureElementById(fixtureId: string) {
|
||||||
|
return document.getElementById(fixtureId) as TestFixtureElement;
|
||||||
|
}
|
||||||
|
|
||||||
// For karma always set our implementation
|
// For karma always set our implementation
|
||||||
// (karma doesn't provide the fixture method)
|
// (karma doesn't provide the fixture method)
|
||||||
window.fixture = function(fixtureId, model) {
|
function fixtureImpl(fixtureId: string, model: unknown) {
|
||||||
// This method is inspired by web-component-tester method
|
// This method is inspired by web-component-tester method
|
||||||
cleanups.push(() => document.getElementById(fixtureId).restore());
|
registerTestCleanup(() => getFixtureElementById(fixtureId).restore());
|
||||||
return document.getElementById(fixtureId).create(model);
|
return getFixtureElementById(fixtureId).create(model);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
window.fixture = fixtureImpl;
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
window.Gerrit = {};
|
window.Gerrit = {};
|
||||||
@@ -67,16 +97,18 @@ setup(() => {
|
|||||||
|
|
||||||
// If the following asserts fails - then window.stub is
|
// If the following asserts fails - then window.stub is
|
||||||
// overwritten by some other code.
|
// overwritten by some other code.
|
||||||
assert.equal(cleanups.length, 0);
|
assert.equal(getCleanupsCount(), 0);
|
||||||
// The following calls is nessecary to avoid influence of previously executed
|
// The following calls is nessecary to avoid influence of previously executed
|
||||||
// tests.
|
// tests.
|
||||||
TestKeyboardShortcutBinder.push();
|
TestKeyboardShortcutBinder.push();
|
||||||
_testOnlyInitAppContext();
|
_testOnlyInitAppContext();
|
||||||
_testOnly_initGerritPluginApi();
|
_testOnly_initGerritPluginApi();
|
||||||
const mgr = _testOnly_getShortcutManagerInstance();
|
const mgr = _testOnly_getShortcutManagerInstance();
|
||||||
assert.equal(mgr.activeHosts.size, 0);
|
assert.isTrue(mgr._testOnly_isEmpty());
|
||||||
assert.equal(mgr.listeners.size, 0);
|
const selection = document.getSelection();
|
||||||
document.getSelection().removeAllRanges();
|
if (selection) {
|
||||||
|
selection.removeAllRanges();
|
||||||
|
}
|
||||||
const pl = _testOnly_resetPluginLoader();
|
const pl = _testOnly_resetPluginLoader();
|
||||||
// For testing, always init with empty plugin list
|
// For testing, always init with empty plugin list
|
||||||
// Since when serve in gr-app, we always retrieve the list
|
// Since when serve in gr-app, we always retrieve the list
|
||||||
@@ -92,49 +124,66 @@ setup(() => {
|
|||||||
|
|
||||||
// For karma always set our implementation
|
// For karma always set our implementation
|
||||||
// (karma doesn't provide the stub method)
|
// (karma doesn't provide the stub method)
|
||||||
window.stub = function(tagName, implementation) {
|
function stubImpl<T extends keyof HTMLElementTagNameMap>(
|
||||||
|
tagName: T,
|
||||||
|
implementation: Partial<HTMLElementTagNameMap[T]>
|
||||||
|
) {
|
||||||
// This method is inspired by web-component-tester method
|
// This method is inspired by web-component-tester method
|
||||||
const proto = document.createElement(tagName).constructor.prototype;
|
const proto = document.createElement(tagName).constructor
|
||||||
const stubs = Object.keys(implementation)
|
.prototype as HTMLElementTagNameMap[T];
|
||||||
.map(key => sinon.stub(proto, key).callsFake(implementation[key]));
|
let key: keyof HTMLElementTagNameMap[T];
|
||||||
cleanups.push(() => {
|
const stubs: SinonSpy[] = [];
|
||||||
|
for (key in implementation) {
|
||||||
|
if (hasOwnProperty(implementation, key)) {
|
||||||
|
stubs.push(sinon.stub(proto, key).callsFake(implementation[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerTestCleanup(() => {
|
||||||
stubs.forEach(stub => {
|
stubs.forEach(stub => {
|
||||||
stub.restore();
|
stub.restore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
window.stub = stubImpl;
|
||||||
|
|
||||||
// Very simple function to catch unexpected elements in documents body.
|
// Very simple function to catch unexpected elements in documents body.
|
||||||
// It can't catch everything, but in most cases it is enough.
|
// It can't catch everything, but in most cases it is enough.
|
||||||
function checkChildAllowed(element) {
|
function checkChildAllowed(element: Element) {
|
||||||
const allowedTags = ['SCRIPT', 'IRON-A11Y-ANNOUNCER'];
|
const allowedTags = ['SCRIPT', 'IRON-A11Y-ANNOUNCER'];
|
||||||
if (allowedTags.includes(element.tagName)) {
|
if (allowedTags.includes(element.tagName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (element.tagName === 'TEST-FIXTURE') {
|
if (element.tagName === 'TEST-FIXTURE') {
|
||||||
if (element.children.length == 0 ||
|
if (
|
||||||
(element.children.length == 1 &&
|
element.children.length === 0 ||
|
||||||
element.children[0].tagName === 'TEMPLATE')) {
|
(element.children.length === 1 &&
|
||||||
|
element.children[0].tagName === 'TEMPLATE')
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert.fail(`Test fixture
|
assert.fail(
|
||||||
|
`Test fixture
|
||||||
${element.outerHTML}` +
|
${element.outerHTML}` +
|
||||||
`isn't resotred after the test is finished. Please ensure that ` +
|
"isn't resotred after the test is finished. Please ensure that " +
|
||||||
`restore() method is called for this test-fixture. Usually the call` +
|
'restore() method is called for this test-fixture. Usually the call' +
|
||||||
`happens automatically.`);
|
'happens automatically.'
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (element.tagName === 'DIV' && element.id === 'gr-hovercard-container' &&
|
if (
|
||||||
element.childNodes.length === 0) {
|
element.tagName === 'DIV' &&
|
||||||
|
element.id === 'gr-hovercard-container' &&
|
||||||
|
element.childNodes.length === 0
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert.fail(
|
assert.fail(
|
||||||
`The following node remains in document after the test:
|
`The following node remains in document after the test:
|
||||||
${element.tagName}
|
${element.tagName}
|
||||||
Outer HTML:
|
Outer HTML:
|
||||||
${element.outerHTML},
|
${element.outerHTML}`
|
||||||
Stack trace:
|
);
|
||||||
${element.stackTrace}`);
|
|
||||||
}
|
}
|
||||||
function checkGlobalSpace() {
|
function checkGlobalSpace() {
|
||||||
for (const child of document.body.children) {
|
for (const child of document.body.children) {
|
||||||
@@ -145,8 +194,6 @@ function checkGlobalSpace() {
|
|||||||
teardown(() => {
|
teardown(() => {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
cleanupTestUtils();
|
cleanupTestUtils();
|
||||||
cleanups.forEach(cleanup => cleanup());
|
|
||||||
cleanups.splice(0);
|
|
||||||
TestKeyboardShortcutBinder.pop();
|
TestKeyboardShortcutBinder.pop();
|
||||||
checkGlobalSpace();
|
checkGlobalSpace();
|
||||||
// Clean Polymer debouncer queue, so next tests will not be affected.
|
// Clean Polymer debouncer queue, so next tests will not be affected.
|
||||||
@@ -15,6 +15,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Mark the file as a module. Otherwise typescript assumes this is a script
|
||||||
|
// and doesn't allow "declare global".
|
||||||
|
// See: https://www.typescriptlang.org/docs/handbook/modules.html
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
sourceMapSupport: {
|
||||||
|
install(): void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The karma.conf.js file loads required module before any other modules
|
// The karma.conf.js file loads required module before any other modules
|
||||||
// The source-map-support.js can't be imported with import ... statement
|
// The source-map-support.js can't be imported with import ... statement
|
||||||
window.sourceMapSupport.install();
|
window.sourceMapSupport.install();
|
||||||
@@ -16,14 +16,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Init app context before any other imports
|
// Init app context before any other imports
|
||||||
import {initAppContext} from '../services/app-context-init.js';
|
import {initAppContext} from '../services/app-context-init';
|
||||||
import {grReportingMock} from '../services/gr-reporting/gr-reporting_mock.js';
|
import {grReportingMock} from '../services/gr-reporting/gr-reporting_mock';
|
||||||
import {appContext} from '../services/app-context.js';
|
import {AppContext, appContext} from '../services/app-context';
|
||||||
|
|
||||||
export function _testOnlyInitAppContext() {
|
export function _testOnlyInitAppContext() {
|
||||||
initAppContext();
|
initAppContext();
|
||||||
|
|
||||||
function setMock(serviceName, setupMock) {
|
function setMock<T extends keyof AppContext>(
|
||||||
|
serviceName: T,
|
||||||
|
setupMock: AppContext[T]
|
||||||
|
) {
|
||||||
Object.defineProperty(appContext, serviceName, {
|
Object.defineProperty(appContext, serviceName, {
|
||||||
get() {
|
get() {
|
||||||
return setupMock;
|
return setupMock;
|
||||||
407
polygerrit-ui/app/test/test-data-generators.ts
Normal file
407
polygerrit-ui/app/test/test-data-generators.ts
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
AccountId,
|
||||||
|
AccountInfo,
|
||||||
|
AccountsConfigInfo,
|
||||||
|
ApprovalInfo,
|
||||||
|
AuthInfo,
|
||||||
|
BranchName,
|
||||||
|
ChangeConfigInfo,
|
||||||
|
ChangeId,
|
||||||
|
ChangeInfo,
|
||||||
|
ChangeInfoId,
|
||||||
|
ChangeMessageId,
|
||||||
|
ChangeMessageInfo,
|
||||||
|
ChangeViewChangeInfo,
|
||||||
|
CommentLinkInfo,
|
||||||
|
CommentLinks,
|
||||||
|
CommitId,
|
||||||
|
CommitInfo,
|
||||||
|
ConfigInfo,
|
||||||
|
DownloadInfo,
|
||||||
|
EditPatchSetNum,
|
||||||
|
GerritInfo,
|
||||||
|
EmailAddress,
|
||||||
|
GitPersonInfo,
|
||||||
|
GitRef,
|
||||||
|
InheritedBooleanInfo,
|
||||||
|
MaxObjectSizeLimitInfo,
|
||||||
|
MergeableInfo,
|
||||||
|
NumericChangeId,
|
||||||
|
PatchSetNum,
|
||||||
|
PluginConfigInfo,
|
||||||
|
PreferencesInfo,
|
||||||
|
RepoName,
|
||||||
|
Reviewers,
|
||||||
|
RevisionInfo,
|
||||||
|
SchemesInfoMap,
|
||||||
|
ServerInfo,
|
||||||
|
SubmitTypeInfo,
|
||||||
|
SuggestInfo,
|
||||||
|
Timestamp,
|
||||||
|
TimezoneOffset,
|
||||||
|
UserConfigInfo,
|
||||||
|
} from '../types/common';
|
||||||
|
import {
|
||||||
|
AccountsVisibility,
|
||||||
|
AppTheme,
|
||||||
|
AuthType,
|
||||||
|
ChangeStatus,
|
||||||
|
DateFormat,
|
||||||
|
DefaultBase,
|
||||||
|
DefaultDisplayNameConfig,
|
||||||
|
DiffViewMode,
|
||||||
|
EmailStrategy,
|
||||||
|
InheritedBooleanInfoConfiguredValue,
|
||||||
|
MergeabilityComputationBehavior,
|
||||||
|
RevisionKind,
|
||||||
|
SubmitType,
|
||||||
|
TimeFormat,
|
||||||
|
} from '../constants/constants';
|
||||||
|
import {formatDate} from '../utils/date-util';
|
||||||
|
import {GetDiffCommentsOutput} from '../services/services/gr-rest-api/gr-rest-api';
|
||||||
|
import {AppElementChangeViewParams} from '../elements/gr-app-types';
|
||||||
|
import {GerritView} from '../elements/core/gr-navigation/gr-navigation';
|
||||||
|
import {
|
||||||
|
EditRevisionInfo,
|
||||||
|
ParsedChangeInfo,
|
||||||
|
} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
||||||
|
|
||||||
|
export function dateToTimestamp(date: Date): Timestamp {
|
||||||
|
const nanosecondSuffix = '.000000000';
|
||||||
|
return (formatDate(date, 'YYYY-MM-DD HH:mm:ss') +
|
||||||
|
nanosecondSuffix) as Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCommentLink(match = 'test'): CommentLinkInfo {
|
||||||
|
return {
|
||||||
|
match,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createInheritedBoolean(value = false): InheritedBooleanInfo {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
configured_value: value
|
||||||
|
? InheritedBooleanInfoConfiguredValue.TRUE
|
||||||
|
: InheritedBooleanInfoConfiguredValue.FALSE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMaxObjectSizeLimit(): MaxObjectSizeLimitInfo {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSubmitType(
|
||||||
|
value: Exclude<SubmitType, SubmitType.INHERIT> = SubmitType.MERGE_IF_NECESSARY
|
||||||
|
): SubmitTypeInfo {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
configured_value: SubmitType.INHERIT,
|
||||||
|
inherited_value: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCommentLinks(): CommentLinks {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createConfig(): ConfigInfo {
|
||||||
|
return {
|
||||||
|
private_by_default: createInheritedBoolean(),
|
||||||
|
work_in_progress_by_default: createInheritedBoolean(),
|
||||||
|
max_object_size_limit: createMaxObjectSizeLimit(),
|
||||||
|
default_submit_type: createSubmitType(),
|
||||||
|
submit_type: SubmitType.INHERIT,
|
||||||
|
commentlinks: createCommentLinks(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAccountWithId(id = 5): AccountInfo {
|
||||||
|
return {
|
||||||
|
_account_id: id as AccountId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAccountWithEmail(email = 'test@'): AccountInfo {
|
||||||
|
return {
|
||||||
|
email: email as EmailAddress,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAccountWithIdNameAndEmail(id = 5): AccountInfo {
|
||||||
|
return {
|
||||||
|
_account_id: id as AccountId,
|
||||||
|
email: `user-${id}@` as EmailAddress,
|
||||||
|
name: `User-${id}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createReviewers(): Reviewers {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TEST_PROJECT_NAME: RepoName = 'test-project' as RepoName;
|
||||||
|
export const TEST_BRANCH_ID: BranchName = 'test-branch' as BranchName;
|
||||||
|
export const TEST_CHANGE_ID: ChangeId = 'TestChangeId' as ChangeId;
|
||||||
|
export const TEST_CHANGE_INFO_ID: ChangeInfoId = `${TEST_PROJECT_NAME}~${TEST_BRANCH_ID}~${TEST_CHANGE_ID}` as ChangeInfoId;
|
||||||
|
export const TEST_SUBJECT = 'Test subject';
|
||||||
|
export const TEST_NUMERIC_CHANGE_ID = 42 as NumericChangeId;
|
||||||
|
|
||||||
|
export const TEST_CHANGE_CREATED = new Date(2020, 1, 1, 1, 2, 3);
|
||||||
|
export const TEST_CHANGE_UPDATED = new Date(2020, 10, 6, 5, 12, 34);
|
||||||
|
|
||||||
|
export function createGitPerson(name = 'Test name'): GitPersonInfo {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
email: `${name}@`,
|
||||||
|
date: dateToTimestamp(new Date(2019, 11, 6, 14, 5, 8)),
|
||||||
|
tz: 0 as TimezoneOffset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCommit(): CommitInfo {
|
||||||
|
return {
|
||||||
|
parents: [],
|
||||||
|
author: createGitPerson(),
|
||||||
|
committer: createGitPerson(),
|
||||||
|
subject: 'Test commit subject',
|
||||||
|
message: 'Test commit message',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRevision(patchSetNum = 1): RevisionInfo {
|
||||||
|
return {
|
||||||
|
_number: patchSetNum as PatchSetNum,
|
||||||
|
commit: createCommit(),
|
||||||
|
created: dateToTimestamp(TEST_CHANGE_CREATED),
|
||||||
|
kind: RevisionKind.REWORK,
|
||||||
|
ref: 'refs/changes/5/6/1' as GitRef,
|
||||||
|
uploader: createAccountWithId(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEditRevision(): EditRevisionInfo {
|
||||||
|
return {
|
||||||
|
_number: EditPatchSetNum,
|
||||||
|
basePatchNum: 1 as PatchSetNum,
|
||||||
|
commit: createCommit(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChangeMessage(id = 'cm_id_1'): ChangeMessageInfo {
|
||||||
|
return {
|
||||||
|
id: id as ChangeMessageId,
|
||||||
|
date: dateToTimestamp(TEST_CHANGE_CREATED),
|
||||||
|
message: `This is a message with id ${id}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRevisions(
|
||||||
|
count: number
|
||||||
|
): {[revisionId: string]: RevisionInfo} {
|
||||||
|
const revisions: {[revisionId: string]: RevisionInfo} = {};
|
||||||
|
const revisionDate = TEST_CHANGE_CREATED;
|
||||||
|
const revisionIdStart = 1; // The same as getCurrentRevision
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const revisionId = (i + revisionIdStart).toString(16);
|
||||||
|
const revision: RevisionInfo = {
|
||||||
|
...createRevision(i + 1),
|
||||||
|
created: dateToTimestamp(revisionDate),
|
||||||
|
ref: `refs/changes/5/6/${i + 1}` as GitRef,
|
||||||
|
};
|
||||||
|
revisions[revisionId] = revision;
|
||||||
|
// advance 1 day
|
||||||
|
revisionDate.setDate(revisionDate.getDate() + 1);
|
||||||
|
}
|
||||||
|
return revisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentRevision(count: number): CommitId {
|
||||||
|
const revisionIdStart = 1; // The same as createRevisions
|
||||||
|
return (count + revisionIdStart).toString(16) as CommitId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChangeMessages(count: number): ChangeMessageInfo[] {
|
||||||
|
const messageIdStart = 1000;
|
||||||
|
const messages: ChangeMessageInfo[] = [];
|
||||||
|
const messageDate = TEST_CHANGE_CREATED;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
messages.push({
|
||||||
|
...createChangeMessage((i + messageIdStart).toString(16)),
|
||||||
|
date: dateToTimestamp(messageDate),
|
||||||
|
});
|
||||||
|
messageDate.setDate(messageDate.getDate() + 1);
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChange(): ChangeInfo {
|
||||||
|
return {
|
||||||
|
id: TEST_CHANGE_INFO_ID,
|
||||||
|
project: TEST_PROJECT_NAME,
|
||||||
|
branch: TEST_BRANCH_ID,
|
||||||
|
change_id: TEST_CHANGE_ID,
|
||||||
|
subject: TEST_SUBJECT,
|
||||||
|
status: ChangeStatus.NEW,
|
||||||
|
created: dateToTimestamp(TEST_CHANGE_CREATED),
|
||||||
|
updated: dateToTimestamp(TEST_CHANGE_UPDATED),
|
||||||
|
insertions: 0,
|
||||||
|
deletions: 0,
|
||||||
|
_number: TEST_NUMERIC_CHANGE_ID,
|
||||||
|
owner: createAccountWithId(),
|
||||||
|
// This is documented as optional, but actually always set.
|
||||||
|
reviewers: createReviewers(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChangeViewChange(): ChangeViewChangeInfo {
|
||||||
|
return {
|
||||||
|
...createChange(),
|
||||||
|
revisions: {
|
||||||
|
abc: createRevision(),
|
||||||
|
},
|
||||||
|
current_revision: 'abc' as CommitId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createParsedChange(): ParsedChangeInfo {
|
||||||
|
return createChangeViewChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAccountsConfig(): AccountsConfigInfo {
|
||||||
|
return {
|
||||||
|
visibility: AccountsVisibility.ALL,
|
||||||
|
default_display_name: DefaultDisplayNameConfig.FULL_NAME,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAuth(): AuthInfo {
|
||||||
|
return {
|
||||||
|
auth_type: AuthType.OPENID,
|
||||||
|
editable_account_fields: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createChangeConfig(): ChangeConfigInfo {
|
||||||
|
return {
|
||||||
|
large_change: 500,
|
||||||
|
reply_label: 'Reply',
|
||||||
|
reply_tooltip: 'Reply and score',
|
||||||
|
// The default update_delay is 5 minutes, but we don't want to accidentally
|
||||||
|
// start polling in tests
|
||||||
|
update_delay: 0,
|
||||||
|
mergeability_computation_behavior:
|
||||||
|
MergeabilityComputationBehavior.REF_UPDATED_AND_CHANGE_REINDEX,
|
||||||
|
enable_attention_set: false,
|
||||||
|
enable_assignee: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDownloadSchemes(): SchemesInfoMap {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDownloadInfo(): DownloadInfo {
|
||||||
|
return {
|
||||||
|
schemes: createDownloadSchemes(),
|
||||||
|
archives: ['tgz', 'tar'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createGerritInfo(): GerritInfo {
|
||||||
|
return {
|
||||||
|
all_projects: 'All-Projects',
|
||||||
|
all_users: 'All-Users',
|
||||||
|
doc_search: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPluginConfig(): PluginConfigInfo {
|
||||||
|
return {
|
||||||
|
has_avatars: false,
|
||||||
|
js_resource_paths: [],
|
||||||
|
html_resource_paths: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSuggestInfo(): SuggestInfo {
|
||||||
|
return {
|
||||||
|
from: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUserConfig(): UserConfigInfo {
|
||||||
|
return {
|
||||||
|
anonymous_coward_name: 'Name of user not set',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createServerInfo(): ServerInfo {
|
||||||
|
return {
|
||||||
|
accounts: createAccountsConfig(),
|
||||||
|
auth: createAuth(),
|
||||||
|
change: createChangeConfig(),
|
||||||
|
download: createDownloadInfo(),
|
||||||
|
gerrit: createGerritInfo(),
|
||||||
|
plugin: createPluginConfig(),
|
||||||
|
suggest: createSuggestInfo(),
|
||||||
|
user: createUserConfig(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createGetDiffCommentsOutput(): GetDiffCommentsOutput {
|
||||||
|
return {
|
||||||
|
baseComments: [],
|
||||||
|
comments: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMergeable(): MergeableInfo {
|
||||||
|
return {
|
||||||
|
submit_type: SubmitType.MERGE_IF_NECESSARY,
|
||||||
|
mergeable: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPreferences(): PreferencesInfo {
|
||||||
|
return {
|
||||||
|
changes_per_page: 10,
|
||||||
|
theme: AppTheme.LIGHT,
|
||||||
|
date_format: DateFormat.ISO,
|
||||||
|
time_format: TimeFormat.HHMM_24,
|
||||||
|
diff_view: DiffViewMode.SIDE_BY_SIDE,
|
||||||
|
my: [],
|
||||||
|
change_table: [],
|
||||||
|
email_strategy: EmailStrategy.ENABLED,
|
||||||
|
default_base_for_merges: DefaultBase.AUTO_MERGE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createApproval(): ApprovalInfo {
|
||||||
|
return createAccountWithId();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAppElementChangeViewParams(): AppElementChangeViewParams {
|
||||||
|
return {
|
||||||
|
view: GerritView.CHANGE,
|
||||||
|
changeNum: TEST_NUMERIC_CHANGE_ID,
|
||||||
|
project: TEST_PROJECT_NAME,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -14,6 +14,15 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {GerritNav} from '../elements/core/gr-navigation/gr-navigation.js';
|
import {GerritNav} from '../elements/core/gr-navigation/gr-navigation';
|
||||||
|
|
||||||
GerritNav.setup(url => { /* noop */ }, params => '', () => []);
|
GerritNav.setup(
|
||||||
|
() => {
|
||||||
|
/* noop */
|
||||||
|
},
|
||||||
|
() => '',
|
||||||
|
() => [],
|
||||||
|
() => {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -14,48 +14,62 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import '../types/globals';
|
||||||
|
import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader';
|
||||||
|
import {testOnly_resetInternalState} from '../elements/shared/gr-js-api-interface/gr-api-utils';
|
||||||
|
import {_testOnly_resetEndpoints} from '../elements/shared/gr-js-api-interface/gr-plugin-endpoints';
|
||||||
|
import {
|
||||||
|
_testOnly_getShortcutManagerInstance,
|
||||||
|
Shortcut,
|
||||||
|
} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
|
||||||
|
|
||||||
import {_testOnly_resetPluginLoader} from '../elements/shared/gr-js-api-interface/gr-plugin-loader.js';
|
export interface MockPromise extends Promise<unknown> {
|
||||||
import {testOnly_resetInternalState} from '../elements/shared/gr-js-api-interface/gr-api-utils.js';
|
resolve: (value?: unknown) => void;
|
||||||
import {_testOnly_resetEndpoints} from '../elements/shared/gr-js-api-interface/gr-plugin-endpoints.js';
|
}
|
||||||
import {_testOnly_getShortcutManagerInstance} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
|
|
||||||
|
|
||||||
export const mockPromise = () => {
|
export const mockPromise = () => {
|
||||||
let res;
|
let res: (value?: unknown) => void;
|
||||||
const promise = new Promise(resolve => {
|
const promise: MockPromise = new Promise(resolve => {
|
||||||
res = resolve;
|
res = resolve;
|
||||||
});
|
}) as MockPromise;
|
||||||
promise.resolve = res;
|
promise.resolve = res!;
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
export const isHidden = el => getComputedStyle(el).display === 'none';
|
export const isHidden = (el: Element) =>
|
||||||
|
getComputedStyle(el).display === 'none';
|
||||||
|
|
||||||
// Some tests/elements can define its own binding. We want to restore bindings
|
// Some tests/elements can define its own binding. We want to restore bindings
|
||||||
// at the end of the test. The TestKeyboardShortcutBinder store bindings in
|
// at the end of the test. The TestKeyboardShortcutBinder store bindings in
|
||||||
// stack, so it is possible to override bindings in nested suites.
|
// stack, so it is possible to override bindings in nested suites.
|
||||||
export class TestKeyboardShortcutBinder {
|
export class TestKeyboardShortcutBinder {
|
||||||
|
private static stack: TestKeyboardShortcutBinder[] = [];
|
||||||
|
|
||||||
static push() {
|
static push() {
|
||||||
if (!this.stack) {
|
|
||||||
this.stack = [];
|
|
||||||
}
|
|
||||||
const testBinder = new TestKeyboardShortcutBinder();
|
const testBinder = new TestKeyboardShortcutBinder();
|
||||||
this.stack.push(testBinder);
|
this.stack.push(testBinder);
|
||||||
return _testOnly_getShortcutManagerInstance();
|
return _testOnly_getShortcutManagerInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
static pop() {
|
static pop() {
|
||||||
this.stack.pop()._restoreShortcuts();
|
const item = this.stack.pop();
|
||||||
|
if (!item) {
|
||||||
|
throw new Error('stack is empty');
|
||||||
|
}
|
||||||
|
item._restoreShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly originalBinding: Map<Shortcut, string[]>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._originalBinding = new Map(
|
this.originalBinding = new Map(
|
||||||
_testOnly_getShortcutManagerInstance().bindings);
|
_testOnly_getShortcutManagerInstance()._testOnly_getBindings()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_restoreShortcuts() {
|
_restoreShortcuts() {
|
||||||
const bindings = _testOnly_getShortcutManagerInstance().bindings;
|
const bindings = _testOnly_getShortcutManagerInstance()._testOnly_getBindings();
|
||||||
bindings.clear();
|
bindings.clear();
|
||||||
this._originalBinding.forEach((value, key) => {
|
this.originalBinding.forEach((value, key) => {
|
||||||
bindings.set(key, value);
|
bindings.set(key, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,9 +84,15 @@ export const resetPlugins = () => {
|
|||||||
pl.loadPlugins([]);
|
pl.loadPlugins([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanups = [];
|
export type CleanupCallback = () => void;
|
||||||
|
|
||||||
function registerTestCleanup(cleanupCallback) {
|
const cleanups: CleanupCallback[] = [];
|
||||||
|
|
||||||
|
export function getCleanupsCount() {
|
||||||
|
return cleanups.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerTestCleanup(cleanupCallback: CleanupCallback) {
|
||||||
cleanups.push(cleanupCallback);
|
cleanups.push(cleanupCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,48 +101,10 @@ export function cleanupTestUtils() {
|
|||||||
cleanups.splice(0);
|
cleanups.splice(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stubBaseUrl(newUrl) {
|
export function stubBaseUrl(newUrl: string) {
|
||||||
const originalCanonicalPath = window.CANONICAL_PATH;
|
const originalCanonicalPath = window.CANONICAL_PATH;
|
||||||
window.CANONICAL_PATH = newUrl;
|
window.CANONICAL_PATH = newUrl;
|
||||||
registerTestCleanup(() => window.CANONICAL_PATH = originalCanonicalPath);
|
registerTestCleanup(() => (window.CANONICAL_PATH = originalCanonicalPath));
|
||||||
}
|
|
||||||
|
|
||||||
export function generateChange(options) {
|
|
||||||
const change = {
|
|
||||||
_number: 42,
|
|
||||||
project: 'testRepo',
|
|
||||||
};
|
|
||||||
const revisionIdStart = 1;
|
|
||||||
const messageIdStart = 1000;
|
|
||||||
// We want to distinguish between empty arrays/objects and undefined
|
|
||||||
// If an option is not set - the appropriate property is not set
|
|
||||||
// If an options is set - the property always set
|
|
||||||
if (options && typeof options.revisionsCount !== 'undefined') {
|
|
||||||
const revisions = {};
|
|
||||||
for (let i = 0; i < options.revisionsCount; i++) {
|
|
||||||
const revisionId = (i + revisionIdStart).toString(16);
|
|
||||||
revisions[revisionId] = {
|
|
||||||
_number: i+1,
|
|
||||||
commit: {parents: []},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
change.revisions = revisions;
|
|
||||||
}
|
|
||||||
if (options && typeof options.messagesCount !== 'undefined') {
|
|
||||||
const messages = [];
|
|
||||||
for (let i = 0; i < options.messagesCount; i++) {
|
|
||||||
messages.push({
|
|
||||||
id: (i + messageIdStart).toString(16),
|
|
||||||
date: new Date(2020, 1, 1),
|
|
||||||
message: `This is a message N${i + 1}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
change.messages = messages;
|
|
||||||
}
|
|
||||||
if (options && options.status) {
|
|
||||||
change.status = options.status;
|
|
||||||
}
|
|
||||||
return change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +116,8 @@ export function generateChange(options) {
|
|||||||
export function createIronOverlayBackdropStyleEl() {
|
export function createIronOverlayBackdropStyleEl() {
|
||||||
const ironOverlayBackdropStyleEl = document.createElement('style');
|
const ironOverlayBackdropStyleEl = document.createElement('style');
|
||||||
document.head.appendChild(ironOverlayBackdropStyleEl);
|
document.head.appendChild(ironOverlayBackdropStyleEl);
|
||||||
ironOverlayBackdropStyleEl.sheet.insertRule(
|
ironOverlayBackdropStyleEl.sheet!.insertRule(
|
||||||
'body { --iron-overlay-backdrop-opacity: 0; }');
|
'body { --iron-overlay-backdrop-opacity: 0; }'
|
||||||
|
);
|
||||||
return ironOverlayBackdropStyleEl;
|
return ironOverlayBackdropStyleEl;
|
||||||
}
|
}
|
||||||
@@ -37,14 +37,18 @@
|
|||||||
/* Advanced Options */
|
/* Advanced Options */
|
||||||
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true,
|
||||||
|
|
||||||
|
"allowUmdGlobalAccess": true
|
||||||
},
|
},
|
||||||
// With the * pattern (without an extension), only supported files
|
// With the * pattern (without an extension), only supported files
|
||||||
// are included. The supported files are .ts, .tsx, .d.ts.
|
// are included. The supported files are .ts, .tsx, .d.ts.
|
||||||
// If allowJs is set to true, .js and .jsx files are included as well.
|
// If allowJs is set to true, .js and .jsx files are included as well.
|
||||||
// Note: gerrit doesn't have .tsx and .jsx files
|
// Note: gerrit doesn't have .tsx and .jsx files
|
||||||
"include": [
|
"include": [
|
||||||
// This items below must be in sync with the src_dirs list in the BUILD file
|
// Items below must be in sync with the src_dirs list in the BUILD file
|
||||||
|
// Also items must be in sync with tsconfig_bazel.json, tsconfig_bazel_test.json
|
||||||
|
// (include and exclude arrays are overriden when extends)
|
||||||
"constants/**/*",
|
"constants/**/*",
|
||||||
"elements/**/*",
|
"elements/**/*",
|
||||||
"embed/**/*",
|
"embed/**/*",
|
||||||
@@ -56,7 +60,6 @@
|
|||||||
"styles/**/*",
|
"styles/**/*",
|
||||||
"types/**/*",
|
"types/**/*",
|
||||||
"utils/**/*",
|
"utils/**/*",
|
||||||
// Directory for test utils (not included in src_dirs in the BUILD file)
|
|
||||||
"test/**/*"
|
"test/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
29
polygerrit-ui/app/tsconfig_bazel.json
Normal file
29
polygerrit-ui/app/tsconfig_bazel.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"typeRoots": [
|
||||||
|
"../../external/ui_npm/node_modules/@types",
|
||||||
|
"../../external/ui_dev_npm/node_modules/@types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
// Items below must be in sync with the src_dirs list in the BUILD file
|
||||||
|
// Also items must be in sync with tsconfig.json, tsconfig_bazel_test.json
|
||||||
|
// (include and exclude arrays are overriden when extends)
|
||||||
|
"constants/**/*",
|
||||||
|
"elements/**/*",
|
||||||
|
"embed/**/*",
|
||||||
|
"gr-diff/**/*",
|
||||||
|
"mixins/**/*",
|
||||||
|
"samples/**/*",
|
||||||
|
"scripts/**/*",
|
||||||
|
"services/**/*",
|
||||||
|
"styles/**/*",
|
||||||
|
"types/**/*",
|
||||||
|
"utils/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/*_test.ts",
|
||||||
|
"**/*_test.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
32
polygerrit-ui/app/tsconfig_bazel_test.json
Normal file
32
polygerrit-ui/app/tsconfig_bazel_test.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig_bazel.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"typeRoots": [
|
||||||
|
"./test/@types",
|
||||||
|
"../../external/ui_dev_npm/node_modules/@polymer/iron-test-helpers",
|
||||||
|
"../../external/ui_npm/node_modules/@types",
|
||||||
|
"../../external/ui_dev_npm/node_modules/@types"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@polymer/iron-test-helpers/*": ["../../ui_dev_npm/node_modules/@polymer/iron-test-helpers/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
// Items below must be in sync with the src_dirs list in the BUILD file
|
||||||
|
// Also items must be in sync with tsconfig.json, tsconfig_test.json
|
||||||
|
// (include and exclude arrays are overriden when extends)
|
||||||
|
"constants/**/*",
|
||||||
|
"elements/**/*",
|
||||||
|
"embed/**/*",
|
||||||
|
"gr-diff/**/*",
|
||||||
|
"mixins/**/*",
|
||||||
|
"samples/**/*",
|
||||||
|
"scripts/**/*",
|
||||||
|
"services/**/*",
|
||||||
|
"styles/**/*",
|
||||||
|
"types/**/*",
|
||||||
|
"utils/**/*",
|
||||||
|
"test/**/*"
|
||||||
|
],
|
||||||
|
"exclude": []
|
||||||
|
}
|
||||||
@@ -45,6 +45,8 @@ import {
|
|||||||
EmailFormat,
|
EmailFormat,
|
||||||
AuthType,
|
AuthType,
|
||||||
MergeStrategy,
|
MergeStrategy,
|
||||||
|
EditableAccountField,
|
||||||
|
MergeabilityComputationBehavior,
|
||||||
} from '../constants/constants';
|
} from '../constants/constants';
|
||||||
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
|
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
|
||||||
|
|
||||||
@@ -248,7 +250,7 @@ export interface ChangeInfo {
|
|||||||
created: Timestamp;
|
created: Timestamp;
|
||||||
updated: Timestamp;
|
updated: Timestamp;
|
||||||
submitted?: Timestamp;
|
submitted?: Timestamp;
|
||||||
submitter: AccountInfo;
|
submitter?: AccountInfo;
|
||||||
starred?: boolean; // not set if false
|
starred?: boolean; // not set if false
|
||||||
stars?: StarLabel[];
|
stars?: StarLabel[];
|
||||||
reviewed?: boolean; // not set if false
|
reviewed?: boolean; // not set if false
|
||||||
@@ -540,7 +542,7 @@ export interface RevisionInfo {
|
|||||||
fetch?: {[protocol: string]: FetchInfo};
|
fetch?: {[protocol: string]: FetchInfo};
|
||||||
commit?: CommitInfo;
|
commit?: CommitInfo;
|
||||||
files?: {[filename: string]: FileInfo};
|
files?: {[filename: string]: FileInfo};
|
||||||
actions?: ActionInfo[];
|
actions?: ActionNameToActionInfoMap;
|
||||||
reviewed?: boolean;
|
reviewed?: boolean;
|
||||||
commit_with_footers?: boolean;
|
commit_with_footers?: boolean;
|
||||||
push_certificate?: PushCertificateInfo;
|
push_certificate?: PushCertificateInfo;
|
||||||
@@ -736,7 +738,7 @@ export interface VotingRangeInfo {
|
|||||||
/**
|
/**
|
||||||
* The AccountsConfigInfo entity contains information about Gerrit configuration
|
* The AccountsConfigInfo entity contains information about Gerrit configuration
|
||||||
* from the accounts section.
|
* from the accounts section.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#accounts-config-info
|
||||||
*/
|
*/
|
||||||
export interface AccountsConfigInfo {
|
export interface AccountsConfigInfo {
|
||||||
visibility: string;
|
visibility: string;
|
||||||
@@ -750,9 +752,9 @@ export interface AccountsConfigInfo {
|
|||||||
*/
|
*/
|
||||||
export interface AuthInfo {
|
export interface AuthInfo {
|
||||||
auth_type: AuthType; // docs incorrectly names it 'type'
|
auth_type: AuthType; // docs incorrectly names it 'type'
|
||||||
use_contributor_agreements: boolean;
|
use_contributor_agreements?: boolean;
|
||||||
contributor_agreements?: ContributorAgreementInfo;
|
contributor_agreements?: ContributorAgreementInfo;
|
||||||
editable_account_fields: string;
|
editable_account_fields: EditableAccountField[];
|
||||||
login_url?: string;
|
login_url?: string;
|
||||||
login_text?: string;
|
login_text?: string;
|
||||||
switch_account_url?: string;
|
switch_account_url?: string;
|
||||||
@@ -799,17 +801,17 @@ export type CapabilityInfoMap = {[id: string]: CapabilityInfo};
|
|||||||
/**
|
/**
|
||||||
* The ChangeConfigInfo entity contains information about Gerrit configuration
|
* The ChangeConfigInfo entity contains information about Gerrit configuration
|
||||||
* from the change section.
|
* from the change section.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#change-config-info
|
||||||
*/
|
*/
|
||||||
export interface ChangeConfigInfo {
|
export interface ChangeConfigInfo {
|
||||||
allow_blame: boolean;
|
allow_blame?: boolean;
|
||||||
large_change: string;
|
large_change: number;
|
||||||
reply_label: string;
|
reply_label: string;
|
||||||
reply_tooltip: string;
|
reply_tooltip: string;
|
||||||
update_delay: number;
|
update_delay: number;
|
||||||
submit_whole_topic: boolean;
|
submit_whole_topic?: boolean;
|
||||||
disable_private_changes: boolean;
|
disable_private_changes?: boolean;
|
||||||
mergeability_computation_behavior: string;
|
mergeability_computation_behavior: MergeabilityComputationBehavior;
|
||||||
enable_attention_set: boolean;
|
enable_attention_set: boolean;
|
||||||
enable_assignee: boolean;
|
enable_assignee: boolean;
|
||||||
}
|
}
|
||||||
@@ -817,10 +819,10 @@ export interface ChangeConfigInfo {
|
|||||||
/**
|
/**
|
||||||
* The ChangeIndexConfigInfo entity contains information about Gerrit
|
* The ChangeIndexConfigInfo entity contains information about Gerrit
|
||||||
* configuration from the index.change section.
|
* configuration from the index.change section.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#change-index-config-info
|
||||||
*/
|
*/
|
||||||
export interface ChangeIndexConfigInfo {
|
export interface ChangeIndexConfigInfo {
|
||||||
index_mergeable: boolean;
|
index_mergeable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -906,11 +908,11 @@ export type SchemesInfoMap = {[name: string]: DownloadSchemeInfo};
|
|||||||
/**
|
/**
|
||||||
* The DownloadInfo entity contains information about supported download
|
* The DownloadInfo entity contains information about supported download
|
||||||
* options.
|
* options.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#download-info
|
||||||
*/
|
*/
|
||||||
export interface DownloadInfo {
|
export interface DownloadInfo {
|
||||||
schemes: SchemesInfoMap;
|
schemes: SchemesInfoMap;
|
||||||
archives: string;
|
archives: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CloneCommandMap = {[name: string]: string};
|
export type CloneCommandMap = {[name: string]: string};
|
||||||
@@ -954,9 +956,9 @@ export interface EntriesInfo {
|
|||||||
export interface GerritInfo {
|
export interface GerritInfo {
|
||||||
all_projects: string; // Doc contains incorrect name
|
all_projects: string; // Doc contains incorrect name
|
||||||
all_users: string; // Doc contains incorrect name
|
all_users: string; // Doc contains incorrect name
|
||||||
doc_search: string;
|
doc_search: boolean;
|
||||||
doc_url?: string;
|
doc_url?: string;
|
||||||
edit_gpg_keys: boolean;
|
edit_gpg_keys?: boolean;
|
||||||
report_bug_url?: string;
|
report_bug_url?: string;
|
||||||
// The following property is missed in doc
|
// The following property is missed in doc
|
||||||
primary_weblink_name?: string;
|
primary_weblink_name?: string;
|
||||||
@@ -965,7 +967,7 @@ export interface GerritInfo {
|
|||||||
/**
|
/**
|
||||||
* The IndexConfigInfo entity contains information about Gerrit configuration
|
* The IndexConfigInfo entity contains information about Gerrit configuration
|
||||||
* from the index section.
|
* from the index section.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#index-config-info
|
||||||
*/
|
*/
|
||||||
export interface IndexConfigInfo {
|
export interface IndexConfigInfo {
|
||||||
change: ChangeIndexConfigInfo;
|
change: ChangeIndexConfigInfo;
|
||||||
@@ -1022,10 +1024,11 @@ export interface MemSummaryInfo {
|
|||||||
/**
|
/**
|
||||||
* The PluginConfigInfo entity contains information about Gerrit extensions by
|
* The PluginConfigInfo entity contains information about Gerrit extensions by
|
||||||
* plugins.
|
* plugins.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#plugin-config-info
|
||||||
*/
|
*/
|
||||||
export interface PluginConfigInfo {
|
export interface PluginConfigInfo {
|
||||||
has_avatars: boolean;
|
has_avatars: boolean;
|
||||||
|
// The following 2 properies exists in Java class, but don't mention in docs
|
||||||
js_resource_paths: string[];
|
js_resource_paths: string[];
|
||||||
html_resource_paths: string[];
|
html_resource_paths: string[];
|
||||||
}
|
}
|
||||||
@@ -1050,7 +1053,8 @@ export interface ServerInfo {
|
|||||||
change: ChangeConfigInfo;
|
change: ChangeConfigInfo;
|
||||||
download: DownloadInfo;
|
download: DownloadInfo;
|
||||||
gerrit: GerritInfo;
|
gerrit: GerritInfo;
|
||||||
index: IndexConfigInfo;
|
// docs mentions index property, but it doesn't exists in Java class
|
||||||
|
// index: IndexConfigInfo;
|
||||||
note_db_enabled?: boolean;
|
note_db_enabled?: boolean;
|
||||||
plugin: PluginConfigInfo;
|
plugin: PluginConfigInfo;
|
||||||
receive?: ReceiveInfo;
|
receive?: ReceiveInfo;
|
||||||
@@ -1074,7 +1078,7 @@ export type SshdInfo = {};
|
|||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#suggest-info
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#suggest-info
|
||||||
*/
|
*/
|
||||||
export interface SuggestInfo {
|
export interface SuggestInfo {
|
||||||
from: string;
|
from: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1149,7 +1153,7 @@ export interface TopMenuItemInfo {
|
|||||||
/**
|
/**
|
||||||
* The UserConfigInfo entity contains information about Gerrit configuration
|
* The UserConfigInfo entity contains information about Gerrit configuration
|
||||||
* from the user section.
|
* from the user section.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-config.html#user-config-info
|
||||||
*/
|
*/
|
||||||
export interface UserConfigInfo {
|
export interface UserConfigInfo {
|
||||||
anonymous_coward_name: string;
|
anonymous_coward_name: string;
|
||||||
@@ -1373,7 +1377,7 @@ export interface InheritedBooleanInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The MaxObjectSizeLimitInfo entity contains information about themax object
|
* The MaxObjectSizeLimitInfo entity contains information about the max object
|
||||||
* size limit of a project.
|
* size limit of a project.
|
||||||
* https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#max-object-size-limit-info
|
* https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#max-object-size-limit-info
|
||||||
*/
|
*/
|
||||||
@@ -2040,8 +2044,8 @@ export interface EditInfo {
|
|||||||
base_patch_set_number: PatchSetNum;
|
base_patch_set_number: PatchSetNum;
|
||||||
base_revision: string;
|
base_revision: string;
|
||||||
ref: GitRef;
|
ref: GitRef;
|
||||||
fetch: ProtocolToFetchInfoMap;
|
fetch?: ProtocolToFetchInfoMap;
|
||||||
files: FileNameToFileInfoMap;
|
files?: FileNameToFileInfoMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProtocolToFetchInfoMap = {[protocol: string]: FetchInfo};
|
export type ProtocolToFetchInfoMap = {[protocol: string]: FetchInfo};
|
||||||
|
|||||||
@@ -142,6 +142,18 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShowAlertEventDetail {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ShowAlertEvent = CustomEvent<ShowAlertEventDetail>;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementEventMap {
|
||||||
|
'show-alert': ShowAlertEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard events emitted from polymer elements.
|
* Keyboard events emitted from polymer elements.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,50 +75,50 @@ declare global {
|
|||||||
// TODO(TS): should clean up those and removing them may break certain plugin behaviors
|
// TODO(TS): should clean up those and removing them may break certain plugin behaviors
|
||||||
// TODO(TS): as @brohlfs suggested, to avoid importing anything from elements/ to types/
|
// TODO(TS): as @brohlfs suggested, to avoid importing anything from elements/ to types/
|
||||||
// use any for them for now
|
// use any for them for now
|
||||||
GrDisplayNameUtils: any;
|
GrDisplayNameUtils: unknown;
|
||||||
GrAnnotation: any;
|
GrAnnotation: unknown;
|
||||||
GrAttributeHelper: any;
|
GrAttributeHelper: unknown;
|
||||||
GrDiffLine: any;
|
GrDiffLine: unknown;
|
||||||
GrDiffLineType: any;
|
GrDiffLineType: unknown;
|
||||||
GrDiffGroup: any;
|
GrDiffGroup: unknown;
|
||||||
GrDiffGroupType: any;
|
GrDiffGroupType: unknown;
|
||||||
GrDiffBuilder: any;
|
GrDiffBuilder: unknown;
|
||||||
GrDiffBuilderSideBySide: any;
|
GrDiffBuilderSideBySide: unknown;
|
||||||
GrDiffBuilderImage: any;
|
GrDiffBuilderImage: unknown;
|
||||||
GrDiffBuilderUnified: any;
|
GrDiffBuilderUnified: unknown;
|
||||||
GrDiffBuilderBinary: any;
|
GrDiffBuilderBinary: unknown;
|
||||||
GrChangeActionsInterface: any;
|
GrChangeActionsInterface: unknown;
|
||||||
GrChangeReplyInterface: any;
|
GrChangeReplyInterface: unknown;
|
||||||
GrEditConstants: any;
|
GrEditConstants: unknown;
|
||||||
GrDomHooksManager: any;
|
GrDomHooksManager: unknown;
|
||||||
GrDomHook: any;
|
GrDomHook: unknown;
|
||||||
GrEtagDecorator: any;
|
GrEtagDecorator: unknown;
|
||||||
GrThemeApi: any;
|
GrThemeApi: unknown;
|
||||||
SiteBasedCache: any;
|
SiteBasedCache: unknown;
|
||||||
FetchPromisesCache: any;
|
FetchPromisesCache: unknown;
|
||||||
GrRestApiHelper: any;
|
GrRestApiHelper: unknown;
|
||||||
GrLinkTextParser: any;
|
GrLinkTextParser: unknown;
|
||||||
GrPluginEndpoints: any;
|
GrPluginEndpoints: unknown;
|
||||||
GrReviewerUpdatesParser: any;
|
GrReviewerUpdatesParser: unknown;
|
||||||
GrPopupInterface: any;
|
GrPopupInterface: unknown;
|
||||||
GrCountStringFormatter: any;
|
GrCountStringFormatter: unknown;
|
||||||
GrReviewerSuggestionsProvider: any;
|
GrReviewerSuggestionsProvider: unknown;
|
||||||
util: any;
|
util: unknown;
|
||||||
Auth: any;
|
Auth: unknown;
|
||||||
EventEmitter: any;
|
EventEmitter: unknown;
|
||||||
GrAdminApi: any;
|
GrAdminApi: unknown;
|
||||||
GrAnnotationActionsContext: any;
|
GrAnnotationActionsContext: unknown;
|
||||||
GrAnnotationActionsInterface: any;
|
GrAnnotationActionsInterface: unknown;
|
||||||
GrChangeMetadataApi: any;
|
GrChangeMetadataApi: unknown;
|
||||||
GrEmailSuggestionsProvider: any;
|
GrEmailSuggestionsProvider: unknown;
|
||||||
GrGroupSuggestionsProvider: any;
|
GrGroupSuggestionsProvider: unknown;
|
||||||
GrEventHelper: any;
|
GrEventHelper: unknown;
|
||||||
GrPluginRestApi: any;
|
GrPluginRestApi: unknown;
|
||||||
GrRepoApi: any;
|
GrRepoApi: unknown;
|
||||||
GrSettingsApi: any;
|
GrSettingsApi: unknown;
|
||||||
GrStylesApi: any;
|
GrStylesApi: unknown;
|
||||||
PluginLoader: any;
|
PluginLoader: unknown;
|
||||||
GrPluginActionContext: any;
|
GrPluginActionContext: unknown;
|
||||||
_apiUtils: {};
|
_apiUtils: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,4 +138,9 @@ declare global {
|
|||||||
// TODO(TS): replace with composedPath if possible
|
// TODO(TS): replace with composedPath if possible
|
||||||
readonly path: EventTarget[];
|
readonly path: EventTarget[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Error {
|
||||||
|
lineNumber?: number; // non-standard property
|
||||||
|
columnNumber?: number; // non-standard property
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,28 +15,37 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import '../test/common-test-setup-karma.js';
|
import '../test/common-test-setup-karma';
|
||||||
import {toSortedPermissionsArray} from './access-util.js';
|
import {toSortedPermissionsArray} from './access-util';
|
||||||
|
|
||||||
suite('access-util tests', () => {
|
suite('access-util tests', () => {
|
||||||
test('toSortedPermissionsArray', () => {
|
test('toSortedPermissionsArray', () => {
|
||||||
const rules = {
|
const rules = {
|
||||||
'global:Project-Owners': {
|
'global:Project-Owners': {
|
||||||
action: 'ALLOW', force: false,
|
action: 'ALLOW',
|
||||||
|
force: false,
|
||||||
},
|
},
|
||||||
'4c97682e6ce6b7247f3381b6f1789356666de7f': {
|
'4c97682e6ce6b7247f3381b6f1789356666de7f': {
|
||||||
action: 'ALLOW', force: false,
|
action: 'ALLOW',
|
||||||
|
force: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const expectedResult = [
|
const expectedResult = [
|
||||||
{id: '4c97682e6ce6b7247f3381b6f1789356666de7f', value: {
|
{
|
||||||
action: 'ALLOW', force: false,
|
id: '4c97682e6ce6b7247f3381b6f1789356666de7f',
|
||||||
}},
|
value: {
|
||||||
{id: 'global:Project-Owners', value: {
|
action: 'ALLOW',
|
||||||
action: 'ALLOW', force: false,
|
force: false,
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'global:Project-Owners',
|
||||||
|
value: {
|
||||||
|
action: 'ALLOW',
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
assert.deepEqual(toSortedPermissionsArray(rules), expectedResult);
|
assert.deepEqual(toSortedPermissionsArray(rules), expectedResult);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ export function hasOwnProperty(obj: any, prop: PropertyKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(TS): move to common types once we have type utils
|
// TODO(TS): move to common types once we have type utils
|
||||||
// tslint:disable-next-line:no-any Required for constructor signature.
|
// Required for constructor signature.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type Constructor<T> = new (...args: any[]) => T;
|
export type Constructor<T> = new (...args: any[]) => T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export function formatDate(date: Date, format: string) {
|
|||||||
if (format.includes('ss')) {
|
if (format.includes('ss')) {
|
||||||
options.second = '2-digit';
|
options.second = '2-digit';
|
||||||
}
|
}
|
||||||
|
|
||||||
let locale = 'en-US';
|
let locale = 'en-US';
|
||||||
// Workaround for Chrome 80, en-US is using h24 (midnight is 24:00),
|
// Workaround for Chrome 80, en-US is using h24 (midnight is 24:00),
|
||||||
// en-GB is using h23 (midnight is 00:00)
|
// en-GB is using h23 (midnight is 00:00)
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import {
|
|||||||
ParentPatchSetNum,
|
ParentPatchSetNum,
|
||||||
} from '../types/common';
|
} from '../types/common';
|
||||||
import {RestApiService} from '../services/services/gr-rest-api/gr-rest-api';
|
import {RestApiService} from '../services/services/gr-rest-api/gr-rest-api';
|
||||||
import {ParsedChangeInfo} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
import {
|
||||||
|
EditRevisionInfo,
|
||||||
|
ParsedChangeInfo,
|
||||||
|
} from '../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
@@ -49,10 +52,6 @@ export interface PatchSet {
|
|||||||
wip?: boolean;
|
wip?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RevisionWithSha extends RevisionInfo {
|
|
||||||
sha: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PatchRange {
|
interface PatchRange {
|
||||||
patchNum?: PatchSetNum;
|
patchNum?: PatchSetNum;
|
||||||
basePatchNum?: PatchSetNum;
|
basePatchNum?: PatchSetNum;
|
||||||
@@ -128,7 +127,9 @@ export function getRevisionByPatchNum(
|
|||||||
* doesn't exist.
|
* doesn't exist.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function findEditParentRevision(revisions: RevisionInfo[]) {
|
export function findEditParentRevision(
|
||||||
|
revisions: Array<RevisionInfo | EditRevisionInfo>
|
||||||
|
) {
|
||||||
const editInfo = revisions.find(info => info._number === EditPatchSetNum);
|
const editInfo = revisions.find(info => info._number === EditPatchSetNum);
|
||||||
|
|
||||||
if (!editInfo) {
|
if (!editInfo) {
|
||||||
@@ -144,7 +145,9 @@ export function findEditParentRevision(revisions: RevisionInfo[]) {
|
|||||||
* @return Change edit patch set number or -1.
|
* @return Change edit patch set number or -1.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function findEditParentPatchNum(revisions: RevisionInfo[]) {
|
export function findEditParentPatchNum(
|
||||||
|
revisions: Array<RevisionInfo | EditRevisionInfo>
|
||||||
|
) {
|
||||||
const revisionInfo = findEditParentRevision(revisions);
|
const revisionInfo = findEditParentRevision(revisions);
|
||||||
// finding parent of 'edit' patchset, hence revisionInfo._number cannot be
|
// finding parent of 'edit' patchset, hence revisionInfo._number cannot be
|
||||||
// 'edit' and must be a number
|
// 'edit' and must be a number
|
||||||
@@ -162,7 +165,9 @@ export function findEditParentPatchNum(revisions: RevisionInfo[]) {
|
|||||||
* 3, edit, 2, 1.
|
* 3, edit, 2, 1.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function sortRevisions<T extends RevisionInfo>(revisions: T[]): T[] {
|
export function sortRevisions<T extends RevisionInfo | EditRevisionInfo>(
|
||||||
|
revisions: T[]
|
||||||
|
): T[] {
|
||||||
const editParent: number = findEditParentPatchNum(revisions);
|
const editParent: number = findEditParentPatchNum(revisions);
|
||||||
// Map a normal patchNum to 2 * (patchNum - 1) + 1... I.e. 1 -> 1,
|
// Map a normal patchNum to 2 * (patchNum - 1) + 1... I.e. 1 -> 1,
|
||||||
// 2 -> 3, 3 -> 5, etc.
|
// 2 -> 3, 3 -> 5, etc.
|
||||||
@@ -200,12 +205,10 @@ export function computeAllPatchSets(
|
|||||||
let patchNums: PatchSet[] = [];
|
let patchNums: PatchSet[] = [];
|
||||||
if (change.revisions && Object.keys(change.revisions).length) {
|
if (change.revisions && Object.keys(change.revisions).length) {
|
||||||
const changeRevisions = change.revisions;
|
const changeRevisions = change.revisions;
|
||||||
const revisions: RevisionWithSha[] = Object.keys(change.revisions).map(
|
const revisions = Object.keys(change.revisions).map(sha => {
|
||||||
sha => {
|
return {sha, ...changeRevisions[sha]};
|
||||||
return {sha, ...changeRevisions[sha]};
|
});
|
||||||
}
|
patchNums = sortRevisions(revisions).map(e => {
|
||||||
);
|
|
||||||
patchNums = sortRevisions(revisions).map((e: RevisionWithSha) => {
|
|
||||||
// TODO(kaspern): Mark which patchset an edit was made on, if an
|
// TODO(kaspern): Mark which patchset an edit was made on, if an
|
||||||
// edit exists -- perhaps with a temporary description.
|
// edit exists -- perhaps with a temporary description.
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
"name": "polygerrit-ui-dev-dependencies",
|
"name": "polygerrit-ui-dev-dependencies",
|
||||||
"description": "Gerrit Code Review - Polygerrit dev dependencies",
|
"description": "Gerrit Code Review - Polygerrit dev dependencies",
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"dependencies": {},
|
"dependencies": {
|
||||||
|
"@types/chai": "^4.2.14",
|
||||||
|
"@types/lodash": "^4.14.162",
|
||||||
|
"@types/mocha": "^8.0.3",
|
||||||
|
"@types/sinon": "^9.0.8"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@open-wc/karma-esm": "^2.16.16",
|
"@open-wc/karma-esm": "^2.16.16",
|
||||||
"@polymer/iron-test-helpers": "^3.0.1",
|
"@polymer/iron-test-helpers": "^3.0.1",
|
||||||
|
|||||||
@@ -989,6 +989,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/caniuse-api/-/caniuse-api-3.0.0.tgz#af31cc52062be0ab24583be072fd49b634dcc2fe"
|
resolved "https://registry.yarnpkg.com/@types/caniuse-api/-/caniuse-api-3.0.0.tgz#af31cc52062be0ab24583be072fd49b634dcc2fe"
|
||||||
integrity sha512-wT1VfnScjAftZsvLYaefu/UuwYJdYBwD2JDL2OQd01plGmuAoir5V6HnVHgrfh7zEwcasoiyO2wQ+W58sNh2sw==
|
integrity sha512-wT1VfnScjAftZsvLYaefu/UuwYJdYBwD2JDL2OQd01plGmuAoir5V6HnVHgrfh7zEwcasoiyO2wQ+W58sNh2sw==
|
||||||
|
|
||||||
|
"@types/chai@^4.2.14":
|
||||||
|
version "4.2.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193"
|
||||||
|
integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==
|
||||||
|
|
||||||
"@types/command-line-args@^5.0.0":
|
"@types/command-line-args@^5.0.0":
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.0.0.tgz#484e704d20dbb8754a8f091eee45cdd22bcff28c"
|
resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.0.0.tgz#484e704d20dbb8754a8f091eee45cdd22bcff28c"
|
||||||
@@ -1125,6 +1130,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/koa" "*"
|
"@types/koa" "*"
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.162":
|
||||||
|
version "4.14.162"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.162.tgz#65d78c397e0d883f44afbf1f7ba9867022411470"
|
||||||
|
integrity sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==
|
||||||
|
|
||||||
"@types/lru-cache@^5.1.0":
|
"@types/lru-cache@^5.1.0":
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
|
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
|
||||||
@@ -1140,6 +1150,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||||
|
|
||||||
|
"@types/mocha@^8.0.3":
|
||||||
|
version "8.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.0.3.tgz#51b21b6acb6d1b923bbdc7725c38f9f455166402"
|
||||||
|
integrity sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "14.0.14"
|
version "14.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
||||||
@@ -1175,6 +1190,18 @@
|
|||||||
"@types/express-serve-static-core" "*"
|
"@types/express-serve-static-core" "*"
|
||||||
"@types/mime" "*"
|
"@types/mime" "*"
|
||||||
|
|
||||||
|
"@types/sinon@^9.0.8":
|
||||||
|
version "9.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.8.tgz#1ed0038d356784f75b086104ef83bfd4130bb81b"
|
||||||
|
integrity sha512-IVnI820FZFMGI+u1R+2VdRaD/82YIQTdqLYC9DLPszZuynAJDtCvCtCs3bmyL66s7FqRM3+LPX7DhHnVTaagDw==
|
||||||
|
dependencies:
|
||||||
|
"@types/sinonjs__fake-timers" "*"
|
||||||
|
|
||||||
|
"@types/sinonjs__fake-timers@*":
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae"
|
||||||
|
integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==
|
||||||
|
|
||||||
"@types/whatwg-url@^6.4.0":
|
"@types/whatwg-url@^6.4.0":
|
||||||
version "6.4.0"
|
version "6.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-6.4.0.tgz#1e59b8c64bc0dbdf66d037cf8449d1c3d5270237"
|
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-6.4.0.tgz#1e59b8c64bc0dbdf66d037cf8449d1c3d5270237"
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const jsExt = '.js';
|
|
||||||
|
|
||||||
class NonJsValidator {
|
|
||||||
onProgramEnd(context, node) {
|
|
||||||
}
|
|
||||||
onGoogDeclareModuleId(context, node) {
|
|
||||||
context.report({
|
|
||||||
message: 'goog.declareModuleId is allowed only in .js files',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JsOnlyValidator {
|
|
||||||
onProgramEnd(context, node) {
|
|
||||||
}
|
|
||||||
onGoogDeclareModuleId(context, node) {
|
|
||||||
context.report({
|
|
||||||
message: 'goog.declareModuleId present, but .d.ts file doesn\'t exist. '
|
|
||||||
+ 'Either remove goog.declareModuleId or add the .d.ts file.',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JsWithDtsValidator {
|
|
||||||
constructor() {
|
|
||||||
this._googDeclareModuleIdExists = false;
|
|
||||||
}
|
|
||||||
onProgramEnd(context, node) {
|
|
||||||
if(!this._googDeclareModuleIdExists) {
|
|
||||||
context.report({
|
|
||||||
message: 'goog.declareModuleId(...) is missed. ' +
|
|
||||||
'Either add it or remove the associated .d.ts file.',
|
|
||||||
node: node,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onGoogDeclareModuleId(context, node) {
|
|
||||||
if(this._googDeclareModuleIdExists) {
|
|
||||||
context.report({
|
|
||||||
message: 'Duplicated goog.declareModuleId.',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename = context.getFilename();
|
|
||||||
this._googDeclareModuleIdExists = true;
|
|
||||||
|
|
||||||
const scope = context.getScope();
|
|
||||||
if(scope.type !== 'global' && scope.type !== 'module') {
|
|
||||||
context.report({
|
|
||||||
message: 'goog.declareModuleId is allowed only at the root level.',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
// no return - other problems are possible
|
|
||||||
}
|
|
||||||
if(node.arguments.length !== 1) {
|
|
||||||
context.report({
|
|
||||||
message: 'goog.declareModuleId must have exactly one parameter.',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
if(node.arguments.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const argument = node.arguments[0];
|
|
||||||
if(argument.type !== 'Literal') {
|
|
||||||
context.report({
|
|
||||||
message: 'The argument for the declareModuleId method '
|
|
||||||
+ 'must be a string literal.',
|
|
||||||
node: argument,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pathStart = '/polygerrit-ui/app/';
|
|
||||||
const index = filename.lastIndexOf(pathStart);
|
|
||||||
if(index < 0) {
|
|
||||||
context.report({
|
|
||||||
message: 'The file located outside of polygerrit-ui/app directory. ' +
|
|
||||||
'Please check eslint config.',
|
|
||||||
node: argument,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const expectedName = 'polygerrit.' +
|
|
||||||
filename.slice(index + pathStart.length, -jsExt.length)
|
|
||||||
.replace(/\//g, '.') // Replace all occurrences of '/' with '.'
|
|
||||||
.replace(/-/g, '$2d'); // Replace all occurrences of '-' with '$2d'
|
|
||||||
if(argument.value !== expectedName) {
|
|
||||||
context.report({
|
|
||||||
message: `Invalid module id. It must be '${expectedName}'.`,
|
|
||||||
node: argument,
|
|
||||||
fix: function(fixer) {
|
|
||||||
return fixer.replaceText(argument, `'${expectedName}'`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
meta: {
|
|
||||||
type: 'problem',
|
|
||||||
docs: {
|
|
||||||
description: 'Check that goog.declareModuleId is valid',
|
|
||||||
category: 'TS imports JS errors',
|
|
||||||
recommended: false,
|
|
||||||
},
|
|
||||||
fixable: "code",
|
|
||||||
schema: [],
|
|
||||||
},
|
|
||||||
create: function (context) {
|
|
||||||
let fileValidator;
|
|
||||||
return {
|
|
||||||
Program: function(node) {
|
|
||||||
const filename = context.getFilename();
|
|
||||||
if(filename.endsWith(jsExt)) {
|
|
||||||
const dtsFilename = filename.slice(0, -jsExt.length) + ".d.ts";
|
|
||||||
if(fs.existsSync(dtsFilename)) {
|
|
||||||
fileValidator = new JsWithDtsValidator();
|
|
||||||
} else {
|
|
||||||
fileValidator = new JsOnlyValidator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fileValidator = new NonJsValidator();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Program:exit": function(node) {
|
|
||||||
fileValidator.onProgramEnd(context, node);
|
|
||||||
fileValidator = null;
|
|
||||||
},
|
|
||||||
'ExpressionStatement > CallExpression[callee.property.name="declareModuleId"][callee.object.name="goog"]': function(node) {
|
|
||||||
fileValidator.onGoogDeclareModuleId(context, node);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// While we are migrating to typescript, gerrit can have .d.ts files.
|
|
||||||
// The option "skipLibCheck" is set to true In the tsconfig.json.
|
|
||||||
// This is required, because we want to skip type checking in node_modules
|
|
||||||
// directory - some .d.ts files in 3rd-party modules are incorrect.
|
|
||||||
// Unfortunately, this options also excludes our own .d.ts files from type
|
|
||||||
// checking. This rule reports all .ts errors in a file as tslint errors.
|
|
||||||
|
|
||||||
function getMassageTextFromChain(chainNode, prefix) {
|
|
||||||
let nestedMessages = prefix + chainNode.messageText;
|
|
||||||
if (chainNode.next && chainNode.next.length > 0) {
|
|
||||||
nestedMessages += "\n";
|
|
||||||
for (const node of chainNode.next) {
|
|
||||||
nestedMessages +=
|
|
||||||
getMassageTextFromChain(node, prefix + " ");
|
|
||||||
if(!nestedMessages.endsWith('\n')) {
|
|
||||||
nestedMessages += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nestedMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMessageText(diagnostic) {
|
|
||||||
if (typeof diagnostic.messageText === 'string') {
|
|
||||||
return diagnostic.messageText;
|
|
||||||
}
|
|
||||||
return getMassageTextFromChain(diagnostic.messageText, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDiagnosticStartAndEnd(diagnostic) {
|
|
||||||
if(diagnostic.start) {
|
|
||||||
const file = diagnostic.file;
|
|
||||||
const start = file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
||||||
const length = diagnostic.length ? diagnostic.length : 0;
|
|
||||||
return {
|
|
||||||
start,
|
|
||||||
end: file.getLineAndCharacterOfPosition(diagnostic.start + length),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
start: {line:0, character: 0},
|
|
||||||
end: {line:0, character: 0},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
meta: {
|
|
||||||
type: "problem",
|
|
||||||
docs: {
|
|
||||||
description: "Reports all typescript problems as linter problems",
|
|
||||||
category: ".d.ts",
|
|
||||||
recommended: false
|
|
||||||
},
|
|
||||||
schema: [],
|
|
||||||
},
|
|
||||||
create: function (context) {
|
|
||||||
const program = context.parserServices.program;
|
|
||||||
return {
|
|
||||||
Program: function(node) {
|
|
||||||
const sourceFile =
|
|
||||||
context.parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
||||||
const allDiagnostics = [
|
|
||||||
...program.getDeclarationDiagnostics(sourceFile),
|
|
||||||
...program.getSemanticDiagnostics(sourceFile)];
|
|
||||||
for(const diagnostic of allDiagnostics) {
|
|
||||||
const {start, end } = getDiagnosticStartAndEnd(diagnostic);
|
|
||||||
context.report({
|
|
||||||
message: getMessageText(diagnostic),
|
|
||||||
loc: {
|
|
||||||
start: {
|
|
||||||
line: start.line + 1,
|
|
||||||
column: start.character,
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
line: end.line + 1,
|
|
||||||
column: end.character,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright (C) 2020 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
function checkImportValid(context, node) {
|
|
||||||
const file = context.getFilename();
|
|
||||||
const importSource = node.source.value;
|
|
||||||
|
|
||||||
if(importSource.startsWith('/')) {
|
|
||||||
return {
|
|
||||||
message: 'Do not use absolute path for import.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetFile = path.resolve(path.dirname(file), importSource);
|
|
||||||
const extName = path.extname(targetFile);
|
|
||||||
// There is a polymer.dom.js file, so .dom is not an extension
|
|
||||||
if(extName !== '' && !targetFile.endsWith('polymer.dom')) {
|
|
||||||
return {
|
|
||||||
message: 'Do not specify extensions for import path.',
|
|
||||||
fix: function(fixer) {
|
|
||||||
return fixer.replaceText(node.source, `'${importSource.slice(0, -extName.length)}'`);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!importSource.startsWith('./') && !importSource.startsWith('../')) {
|
|
||||||
// Import from node_modules - nothing else to check
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(fs.existsSync(targetFile + ".ts")) {
|
|
||||||
// .ts file exists - nothing to check
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsFileExists = fs.existsSync(targetFile + '.js');
|
|
||||||
const dtsFileExists = fs.existsSync(targetFile + '.d.ts');
|
|
||||||
|
|
||||||
if(jsFileExists && !dtsFileExists) {
|
|
||||||
return {
|
|
||||||
message: `The '${importSource}.d.ts' file doesn't exist.`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!jsFileExists && dtsFileExists) {
|
|
||||||
return {
|
|
||||||
message: `The '${importSource}.js' file doesn't exist.`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// If both files (.js and .d.ts) don't exist, the error is reported by
|
|
||||||
// the typescript compiler. Do not report anything from the rule.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
meta: {
|
|
||||||
type: "problem",
|
|
||||||
docs: {
|
|
||||||
description: "Check that TS file can import specific JS file",
|
|
||||||
category: "TS imports JS errors",
|
|
||||||
recommended: false
|
|
||||||
},
|
|
||||||
schema: [],
|
|
||||||
fixable: "code",
|
|
||||||
},
|
|
||||||
create: function (context) {
|
|
||||||
return {
|
|
||||||
Program: function(node) {
|
|
||||||
const filename = context.getFilename();
|
|
||||||
if(filename.endsWith('.ts') && !filename.endsWith('.d.ts')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context.report({
|
|
||||||
message: 'The rule must be used only with .ts files. ' +
|
|
||||||
'Check eslint settings.',
|
|
||||||
node: node,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
ImportDeclaration: function (node) {
|
|
||||||
const importProblem = checkImportValid(context, node);
|
|
||||||
if(importProblem) {
|
|
||||||
context.report({
|
|
||||||
message: importProblem.message,
|
|
||||||
node: node.source,
|
|
||||||
fix: importProblem.fix,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user