diff --git a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html index c7bdacc10..142ec850e 100644 --- a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html +++ b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html @@ -356,7 +356,10 @@ total-count="$ctrl.local.orderedRobotAccounts.entries.length" current-page="$ctrl.local.robotOptions.page" page-size="$ctrl.robotsPerPage"> - + diff --git a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.spec.ts b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.spec.ts index 547781352..91a0cf15c 100644 --- a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.spec.ts +++ b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.spec.ts @@ -1,27 +1,30 @@ -import { ManageTriggerGithostComponent, Local, Trigger } from './manage-trigger-githost.component'; +import { ManageTriggerGithostComponent } from './manage-trigger-githost.component'; +import { Local, Trigger, Repository } from '../../../types/common.types'; +import { Mock } from 'ts-mocks'; +import Spy = jasmine.Spy; describe("ManageTriggerGithostComponent", () => { var component: ManageTriggerGithostComponent; - var apiServiceMock: any; - var tableServiceMock: any; - var triggerServiceMock: any; - var rolesServiceMock: any; + var apiServiceMock: Mock; + var tableServiceMock: Mock; + var triggerServiceMock: Mock; + var rolesServiceMock: Mock; var repository: any; var trigger: Trigger; - var $scope: ng.IScope; + var $scopeMock: Mock; beforeEach(inject(($injector: ng.auto.IInjectorService) => { - apiServiceMock = jasmine.createSpyObj('apiServiceMock', ['listTriggerBuildSourceNamespaces']); - tableServiceMock = jasmine.createSpyObj('tableServiceMock', ['buildOrderedItems']); - triggerServiceMock = jasmine.createSpyObj('triggerServiceMock', ['getIcon']); - rolesServiceMock = jasmine.createSpyObj('rolesServiceMock', ['setRepositoryRole']); - $scope = $injector.get('$rootScope'); - component = new ManageTriggerGithostComponent(apiServiceMock, - tableServiceMock, - triggerServiceMock, - rolesServiceMock, - $scope); + apiServiceMock = new Mock(); + tableServiceMock = new Mock(); + triggerServiceMock = new Mock(); + rolesServiceMock = new Mock(); + $scopeMock = new Mock(); + component = new ManageTriggerGithostComponent(apiServiceMock.Object, + tableServiceMock.Object, + triggerServiceMock.Object, + rolesServiceMock.Object, + $scopeMock.Object); trigger = {service: "serviceMock", id: 1}; component.trigger = trigger; })); @@ -36,14 +39,75 @@ describe("ManageTriggerGithostComponent", () => { describe("getTriggerIcon", () => { - it("calls trigger service to get icon", () => { - component.getTriggerIcon(); + beforeEach(() => { + triggerServiceMock.setup(mock => mock.getIcon).is((service: any) => null); + }); - expect(triggerServiceMock.getIcon.calls.argsFor(0)[0]).toEqual(component.trigger.service); + it("calls trigger service to get icon", () => { + const icon: any = component.getTriggerIcon(); + + expect((triggerServiceMock.Object.getIcon).calls.argsFor(0)[0]).toEqual(component.trigger.service); }); }); describe("createTrigger", () => { - // TODO + + beforeEach(() => { + component.local.selectedRepository = new Mock().Object; + component.local.selectedRepository.full_name = "someorg/some-repository"; + component.local.dockerfilePath = "/Dockerfile"; + component.local.dockerContext = "/"; + component.local.triggerOptions = {}; + component.local.triggerAnalysis = {}; + rolesServiceMock.setup(mock => mock.setRepositoryRole).is((repo, role, entityKind, entityName, callback) => { + callback(); + }); + }); + + it("calls roles service to grant read access to selected robot if robot is required and cannot read", (done) => { + component.local.triggerAnalysis = {status: 'requiresrobot', name: 'privatebase', namespace: 'someorg'}; + component.local.robotAccount = {can_read: false, is_robot: true, kind: 'user', name: 'test-robot'}; + component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => { + expect((rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[0]).toEqual({ + name: component.local.triggerAnalysis.name, + namespace: component.local.triggerAnalysis.namespace, + }); + expect((rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[1]).toEqual('read'); + expect((rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[2]).toEqual('robot'); + done(); + }); + + component.createTrigger(); + }); + + it("does not call roles service if robot is required but already has read access", (done) => { + component.local.robotAccount = {can_read: true, is_robot: true, kind: 'user', name: 'test-robot'}; + component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => { + expect((rolesServiceMock.Object.setRepositoryRole)).not.toHaveBeenCalled(); + done(); + }); + + component.createTrigger(); + }); + + it("does not call roles service if robot is not required", (done) => { + component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => { + expect((rolesServiceMock.Object.setRepositoryRole)).not.toHaveBeenCalled(); + done(); + }); + + component.createTrigger(); + }); + + it("emits output event with config and pull robot", (done) => { + component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => { + expect(event.config.build_source).toEqual(component.local.selectedRepository.full_name); + expect(event.config.dockerfile_path).toEqual(component.local.dockerfilePath); + expect(event.config.context).toEqual(component.local.dockerContext); + done(); + }); + + component.createTrigger(); + }); }); }); \ No newline at end of file diff --git a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.ts b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.ts index bc731626b..be7969965 100644 --- a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.ts +++ b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.ts @@ -1,5 +1,6 @@ import { Input, Output, Component, Inject, EventEmitter, OnInit } from 'ng-metadata/core'; import * as moment from 'moment'; +import { Local, Trigger, Repository, Namespace } from '../../../types/common.types'; /** @@ -12,30 +13,14 @@ import * as moment from 'moment'; export class ManageTriggerGithostComponent implements OnInit { // FIXME: Use one-way data binding - @Input('=') public repository: any; + @Input('=') public repository: Repository; @Input('=') public trigger: Trigger; @Output() public activateTrigger: EventEmitter<{config: any, pull_robot?: any}> = new EventEmitter(); - private config: any; - private local: any = { - namespaceOptions: { - filter: '', - predicate: 'score', - reverse: false, - page: 0 - }, - repositoryOptions: { - filter: '', - predicate: 'score', - reverse: false, - page: 0, - hideStale: true - }, - robotOptions: { - filter: '', - predicate: 'score', - reverse: false, - page: 0 - } + public config: any; + public local: Local = { + namespaceOptions: {filter: '', predicate: 'score', reverse: false, page: 0}, + repositoryOptions: {filter: '', predicate: 'score', reverse: false, page: 0, hideStale: true}, + robotOptions: {filter: '', predicate: 'score', reverse: false, page: 0}, }; private currentState: any | null; private namespacesPerPage: number = 10; @@ -128,23 +113,21 @@ export class ManageTriggerGithostComponent implements OnInit { context: this.local.dockerContext }; - if (this.local.triggerOptions.hasBranchTagFilter && - this.local.triggerOptions.branchTagFilter) { - config['branchtag_regex'] = this.local.triggerOptions.branchTagFilter; + if (this.local.triggerOptions['hasBranchTagFilter'] && this.local.triggerOptions['branchTagFilter']) { + config['branchtag_regex'] = this.local.triggerOptions['branchTagFilter']; } - var activate = () => { + const activate = () => { this.activateTrigger.emit({config: config, pull_robot: this.local.robotAccount}); }; - if (this.local.robotAccount && this.local.triggerAnalysis.status == 'requiresrobot') { + if (this.local.triggerAnalysis.status == 'requiresrobot' && this.local.robotAccount) { if (this.local.robotAccount.can_read) { activate(); } else { - // Add read permission onto the base repository for the robot and then activate the - // trigger. - var robot_name = this.local.robotAccount.name; - this.RolesService.setRepositoryRole(this.repository, 'read', 'robot', robot_name, activate); + // Add read permission onto the base repository for the robot and then activate the trigger. + const baseRepo: any = {name: this.local.triggerAnalysis.name, namespace: this.local.triggerAnalysis.namespace}; + this.RolesService.setRepositoryRole(baseRepo, 'read', 'robot', this.local.robotAccount.name, activate); } } else { activate(); @@ -156,7 +139,7 @@ export class ManageTriggerGithostComponent implements OnInit { return; } - var namespaces = this.local.namespaces || []; + var namespaces: Namespace[] = this.local.namespaces || []; this.local.orderedNamespaces = this.TableService.buildOrderedItems(namespaces, this.local.namespaceOptions, ['id'], @@ -305,9 +288,9 @@ export class ManageTriggerGithostComponent implements OnInit { var robots = this.local.triggerAnalysis.robots; this.local.orderedRobotAccounts = this.TableService.buildOrderedItems(robots, - this.local.robotOptions, - ['name'], - []); + this.local.robotOptions, + ['name'], + []); } private checkDockerfilePath(repository: any, path: string, context: string): void { @@ -342,38 +325,3 @@ export class ManageTriggerGithostComponent implements OnInit { } } } - - -/** - * A type representing local application data. - */ -export type Local = { - namespaceOptions: { - filter: string; - predicate: string; - reverse: boolean; - page: number; - }; - repositoryOptions: { - filter: string; - predicate: string; - reverse: boolean; - page: number; - hideStale: boolean; - }; - robotOptions: { - filter: string; - predicate: string; - reverse: boolean; - page: number; - }; -}; - - -/** - * A type representing a trigger. - */ -export type Trigger = { - id: number; - service: any; -}; \ No newline at end of file diff --git a/static/js/types/common.types.ts b/static/js/types/common.types.ts new file mode 100644 index 000000000..316176a34 --- /dev/null +++ b/static/js/types/common.types.ts @@ -0,0 +1,104 @@ +import { ViewArray } from '../services/view-array/view-array'; + + +/** + * A type representing local application data. + */ +export type Local = { + contexts?: string[]; + dockerContext?: string; + dockerfileLocations?: any; + dockerfilePath?: string; + hasValidContextLocation?: boolean; + hasValidDockerfilePath?: boolean; + maxScore?: number; + namespaceOptions?: { + filter: string; + predicate: string; + reverse: boolean; + page: number; + }; + namespaces?: Namespace[]; + orderedNamespaces?: ViewArray; + orderedRepositories?: ViewArray; + orderedRobotAccounts?: ViewArray; + repositories?: Repository[]; + repositoryFullRefs?: { + icon: string; + title: string; + value: string; + }[]; + repositoryOptions?: { + filter: string; + predicate: string; + reverse: boolean; + page: number; + hideStale: boolean; + }; + repositoryRefs?: { + kind: string; + name: string; + }[]; + robotAccount?: RobotAccount; + robotOptions?: { + filter: string; + predicate: string; + reverse: boolean; + page: number; + }; + selectedNamespace?: Namespace; + selectedRepository?: Repository; + triggerAnalysis?: any; + triggerOptions?: { + [key: string]: boolean; + }; +}; + + +/** + * A type representing a robot account. + */ +export type RobotAccount = { + can_read: boolean; + is_robot: boolean; + kind: string; + name: string; +}; + + +/** + * A type representing a Git repository. + */ +export type Repository = { + description: string; + full_name: string; + has_admin_permissions: boolean; + last_updated: number; + last_updated_datetime: Date; + name: string; + private: boolean; + url: string; + namespace?: string; +} + + +/** + * A type representing a repository namespace. + */ +export type Namespace = { + avatar_url: string; + id: string; + personal: boolean; + score: number; + title: string; + url: string; +}; + + +/** + * A type representing a trigger. + */ +export type Trigger = { + id: number; + service: any; +}; \ No newline at end of file diff --git a/test/data/test.db b/test/data/test.db index 914c1c29e..abfe7ef07 100644 Binary files a/test/data/test.db and b/test/data/test.db differ