Dashboard: added the validation of a factory name's uniqueness (#6758)
* CHE-5462: add unique-factory-name validation directive. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> * CHE-5462: use validation directive for factory name's uniqueness. Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com>6.19.x
parent
45f4b35233
commit
a22f44294b
|
|
@ -22,7 +22,7 @@ export class CreateFactoryCtrl {
|
|||
private $log: ng.ILogService;
|
||||
private cheAPI: CheAPI;
|
||||
private cheNotification: CheNotification;
|
||||
private lodash: _.LoDashStatic;
|
||||
private lodash: any;
|
||||
private $filter: ng.IFilterService;
|
||||
private $document: ng.IDocumentService;
|
||||
private isLoading: boolean;
|
||||
|
|
@ -42,7 +42,7 @@ export class CreateFactoryCtrl {
|
|||
* Default constructor that is using resource injection
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($location: ng.ILocationService, cheAPI: CheAPI, $log: ng.ILogService, cheNotification: CheNotification, $scope: ng.IScope, $filter: ng.IFilterService, lodash: _.LoDashStatic, $document: ng.IDocumentService) {
|
||||
constructor($location: ng.ILocationService, cheAPI: CheAPI, $log: ng.ILogService, cheNotification: CheNotification, $scope: ng.IScope, $filter: ng.IFilterService, lodash: any, $document: ng.IDocumentService) {
|
||||
this.$location = $location;
|
||||
this.cheAPI = cheAPI;
|
||||
this.$log = $log;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@
|
|||
ng-trim
|
||||
ng-minlength="3"
|
||||
ng-maxlength="20"
|
||||
ng-pattern="/^[ A-Za-z0-9_\-\.]+$/">
|
||||
ng-pattern="/^[ A-Za-z0-9_\-\.]+$/"
|
||||
unique-factory-name="">
|
||||
<div ng-message="pattern">Factory name may contain digits, latin letters, spaces, _ , . , - and should start only with digits, latin letters or underscores</div>
|
||||
<div ng-message="minlength">The name has to be more than 3 characters long.</div>
|
||||
<div ng-message="maxlength">The name has to be less than 20 characters long.</div>
|
||||
<div ng-message="uniqueFactoryName">This factory name is already used.</div>
|
||||
</che-input-box>
|
||||
</ng-form>
|
||||
<!--Factory source-->
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class FactoryInformationController {
|
|||
private $location: ng.ILocationService;
|
||||
private $log: ng.ILogService;
|
||||
private $timeout: ng.ITimeoutService;
|
||||
private lodash: _.LoDashStatic;
|
||||
private lodash: any;
|
||||
private $filter: ng.IFilterService;
|
||||
|
||||
private timeoutPromise: ng.IPromise<any>;
|
||||
|
|
@ -40,13 +40,14 @@ export class FactoryInformationController {
|
|||
private workspaceName: string;
|
||||
private stackId: string;
|
||||
private workspaceConfig: any;
|
||||
private origName: string;
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource injection
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor($scope: ng.IScope, cheAPI: CheAPI, cheNotification: CheNotification, $location: ng.ILocationService, $log: ng.ILogService,
|
||||
$timeout: ng.ITimeoutService, lodash: _.LoDashStatic, $filter: ng.IFilterService, $q: ng.IQService, confirmDialogService: any) {
|
||||
$timeout: ng.ITimeoutService, lodash: any, $filter: ng.IFilterService, $q: ng.IQService, confirmDialogService: any) {
|
||||
this.cheAPI = cheAPI;
|
||||
this.cheNotification = cheNotification;
|
||||
this.$location = $location;
|
||||
|
|
@ -93,6 +94,7 @@ export class FactoryInformationController {
|
|||
this.environmentName = this.factory.workspace.defaultEnv;
|
||||
|
||||
this.copyOriginFactory = angular.copy(this.factory);
|
||||
this.origName = this.factory.name;
|
||||
if (this.copyOriginFactory.links) {
|
||||
delete this.copyOriginFactory.links;
|
||||
}
|
||||
|
|
@ -100,7 +102,7 @@ export class FactoryInformationController {
|
|||
let factoryContent = this.$filter('json')(this.copyOriginFactory);
|
||||
if (factoryContent !== this.factoryContent) {
|
||||
if (!this.factoryContent) {
|
||||
this.editorLoadedPromise.then((instance) => {
|
||||
this.editorLoadedPromise.then((instance: any) => {
|
||||
this.$timeout(() => {
|
||||
instance.refresh();
|
||||
}, 500);
|
||||
|
|
@ -139,12 +141,29 @@ export class FactoryInformationController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Update factory data.
|
||||
* Update factory name.
|
||||
*
|
||||
* @param {string} name new factory name.
|
||||
* @raram {ng.IFormController} form
|
||||
*/
|
||||
updateFactory(): void {
|
||||
updateName(name: string, form: ng.IFormController): void {
|
||||
if (form.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.copyOriginFactory.name = name;
|
||||
|
||||
this.updateFactory(form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update factory data.
|
||||
* @param {ng.IFormController} form
|
||||
*/
|
||||
updateFactory(form: ng.IFormController): void {
|
||||
this.factoryContent = this.$filter('json')(this.copyOriginFactory);
|
||||
|
||||
if (this.factoryInformationForm.$invalid || !this.isFactoryChanged()) {
|
||||
if (form.$invalid || !this.isFactoryChanged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,16 @@
|
|||
<div layout="column" class="factory-information-input">
|
||||
<ng-form name="factoryInformationForm">
|
||||
<che-input che-form="factoryInformationForm"
|
||||
ng-init="factoryInformationController.factoryInformationForm = factoryInformationForm"
|
||||
che-name="name"
|
||||
che-place-holder="Name of the factory"
|
||||
aria-label="Name of the factory"
|
||||
ng-model="factoryInformationController.copyOriginFactory.name"
|
||||
ng-change="factoryInformationController.updateFactory()"
|
||||
ng-change="factoryInformationController.updateName($value, factoryInformationForm)"
|
||||
ng-trim
|
||||
ng-minlength="3"
|
||||
ng-maxlength="20"
|
||||
ng-pattern="/^[ A-Za-z0-9_\-\.]+$/">
|
||||
ng-pattern="/^[ A-Za-z0-9_\-\.]+$/"
|
||||
unique-factory-name="factoryInformationController.origName">
|
||||
<div ng-message="required">A name is required.</div>
|
||||
<div ng-message="pattern">Factory name may contain digits, latin letters, spaces, _ , . , - and should start
|
||||
only
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
</div>
|
||||
<div ng-message="minlength">The name has to be more than 3 characters long.</div>
|
||||
<div ng-message="maxlength">The name has to be less than 20 characters long.</div>
|
||||
<div ng-message="uniqueFactoryName">This factory name is already used.</div>
|
||||
</che-input>
|
||||
</ng-form>
|
||||
</div>
|
||||
|
|
@ -94,7 +95,7 @@
|
|||
che-place-holder="Name of the workspace"
|
||||
aria-label="Name of the workspace"
|
||||
ng-model="factoryInformationController.copyOriginFactory.workspace.name"
|
||||
ng-change="factoryInformationController.updateFactory()"
|
||||
ng-change="factoryInformationController.updateFactory(factoryInformationForm)"
|
||||
required
|
||||
ng-minlength="3"
|
||||
ng-maxlength="20"
|
||||
|
|
@ -123,7 +124,7 @@
|
|||
<span ng-if="factoryInformationController.getObjectKeys(environmentValue.machines).length > 1">MACHINE: {{machineKey}}</span>
|
||||
<che-workspace-ram-allocation-slider
|
||||
ng-model="machineValue.attributes.memoryLimitBytes"
|
||||
che-on-change="factoryInformationController.updateFactory()"></che-workspace-ram-allocation-slider>
|
||||
che-on-change="factoryInformationController.updateFactory(factoryInformationForm)"></che-workspace-ram-allocation-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -136,7 +137,7 @@
|
|||
<che-label-container che-label-name="Configure Commands"
|
||||
che-label-description="Commands are processes that are invoked by users from a dropdown in the IDE.">
|
||||
<cdvy-factory-command cdvy-factory-object="factoryInformationController.copyOriginFactory"
|
||||
cdvy-on-change="factoryInformationController.updateFactory()"></cdvy-factory-command>
|
||||
cdvy-on-change="factoryInformationController.updateFactory(factoryInformationForm)"></cdvy-factory-command>
|
||||
</che-label-container>
|
||||
|
||||
<!-- Configure actions -->
|
||||
|
|
@ -151,7 +152,7 @@
|
|||
<cdvy-factory-action-box cdvy-lifecycle="onProjectsLoaded"
|
||||
cdvy-callback-controller="factoryInformationController"
|
||||
cdvy-factory-object="factoryInformationController.copyOriginFactory"
|
||||
cdvy-on-change="factoryInformationController.updateFactory()"></cdvy-factory-action-box>
|
||||
cdvy-on-change="factoryInformationController.updateFactory(factoryInformationForm)"></cdvy-factory-action-box>
|
||||
</che-label-container>
|
||||
|
||||
<!-- Configuration -->
|
||||
|
|
|
|||
|
|
@ -532,6 +532,17 @@ export class CheFactory {
|
|||
return this.factoriesById.get(factoryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory by factoryName and userId
|
||||
* @param factoryName {string} the factory name
|
||||
* @param userId {string} the user ID
|
||||
* @returns factory {che.IFactory}
|
||||
*/
|
||||
getFactoryByName(factoryName: string, userId: string): che.IFactory {
|
||||
const key = `${userId}:${factoryName}`;
|
||||
return this.factoriesByName.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the factory
|
||||
* @param factory {che.IFactory}
|
||||
|
|
|
|||
|
|
@ -469,6 +469,9 @@ export class CheHttpBackend {
|
|||
this.httpBackend.when('DELETE', '/api/factory/' + factory.id).respond(() => {
|
||||
return [200, {success: true, errors: []}];
|
||||
});
|
||||
if (this.defaultUser) {
|
||||
this.httpBackend.when('GET', `/api/factory/find?creator.userId=${this.defaultUser.id}&name=${factory.name}`).respond([factory]);
|
||||
}
|
||||
allFactories.push(factory);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@ declare namespace che {
|
|||
ide?: any;
|
||||
button?: any;
|
||||
policies?: any;
|
||||
links: string[];
|
||||
}
|
||||
|
||||
export interface IRegistry {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2017 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
import {CheAPI} from '../api/che-api.factory';
|
||||
|
||||
interface IFactoryNameValidatorAsyncModelValidators extends ng.IAsyncModelValidators {
|
||||
uniqueFactoryName: (modelValue: any, viewValue?: any) => ng.IPromise<any>;
|
||||
}
|
||||
|
||||
interface IFactoryNameValidatorAttributes extends ng.IAttributes {
|
||||
uniqueFactoryName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a directive for checking if the factory name is not already taken
|
||||
* @author Oleksii Kurinnyi
|
||||
*/
|
||||
export class UniqueFactoryNameValidator implements ng.IDirective {
|
||||
$q: ng.IQService;
|
||||
cheAPI: CheAPI;
|
||||
|
||||
restrict: string = 'A';
|
||||
require: string = 'ngModel';
|
||||
|
||||
user: che.IUser;
|
||||
|
||||
/**
|
||||
* Default constructor that is using resource
|
||||
* @ngInject for Dependency injection
|
||||
*/
|
||||
constructor (cheAPI: CheAPI, $q: ng.IQService) {
|
||||
this.cheAPI = cheAPI;
|
||||
this.$q = $q;
|
||||
|
||||
this.user = this.cheAPI.getUser().getUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the name of workspace is unique
|
||||
*/
|
||||
link($scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: IFactoryNameValidatorAttributes, ngModel: ng.INgModelController) {
|
||||
|
||||
const asyncValidators = ngModel.$asyncValidators as IFactoryNameValidatorAsyncModelValidators;
|
||||
|
||||
// validate only input element
|
||||
if ('input' === element[0].localName) {
|
||||
|
||||
asyncValidators.uniqueFactoryName = (modelValue: any, viewValue: any) => {
|
||||
|
||||
// create promise
|
||||
const deferred = this.$q.defer();
|
||||
|
||||
if (!this.user) {
|
||||
deferred.reject(false);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// parent scope ?
|
||||
let scopingTest = $scope.$parent;
|
||||
if (!scopingTest) {
|
||||
scopingTest = $scope;
|
||||
}
|
||||
|
||||
const currentFactoryName = scopingTest.$eval(attributes.uniqueFactoryName);
|
||||
|
||||
if (!modelValue || modelValue === currentFactoryName) {
|
||||
deferred.resolve(true);
|
||||
} else if (this.cheAPI.getFactory().getFactoryByName(modelValue, this.user.id)) {
|
||||
deferred.reject(false);
|
||||
} else {
|
||||
this.cheAPI.getFactory().fetchFactoryByName(modelValue, this.user.id).finally(() => {
|
||||
if (this.cheAPI.getFactory().getFactoryByName(modelValue, this.user.id)) {
|
||||
deferred.reject(false);
|
||||
} else {
|
||||
deferred.resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// return promise
|
||||
return deferred.promise;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) 2015-2017 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
'use strict';
|
||||
import {CheFactory} from '../api/che-factory.factory';
|
||||
import {CheAPIBuilder} from '../api/builder/che-api-builder.factory';
|
||||
import {CheHttpBackend} from '../api/test/che-http-backend';
|
||||
|
||||
interface ITestRootScope extends ng.IRootScopeService {
|
||||
factoryName: string;
|
||||
myForm: ng.IFormController;
|
||||
}
|
||||
|
||||
interface ITestFormController extends ng.IFormController {
|
||||
name: ng.INgModelController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the factory name uniqueness
|
||||
* @author Oleksii Kurinnyi
|
||||
*/
|
||||
|
||||
describe('unique-factory-name-validator >', function() {
|
||||
let $rootScope: ITestRootScope,
|
||||
$compile: ng.ICompileService,
|
||||
myForm: ITestFormController;
|
||||
|
||||
/**
|
||||
* Factory API
|
||||
*/
|
||||
let cheFactory: CheFactory;
|
||||
|
||||
/**
|
||||
* API builder.
|
||||
*/
|
||||
let cheAPIBuilder: CheAPIBuilder;
|
||||
|
||||
/**
|
||||
* Che backend
|
||||
*/
|
||||
let cheHttpBackend: CheHttpBackend;
|
||||
|
||||
/**
|
||||
* Backend for handling http operations
|
||||
*/
|
||||
let $httpBackend: ng.IHttpBackendService;
|
||||
|
||||
beforeEach(angular.mock.module('userDashboard'));
|
||||
|
||||
beforeEach(inject((_$compile_: ng.ICompileService,
|
||||
_$rootScope_: ITestRootScope,
|
||||
_cheFactory_: CheFactory,
|
||||
_cheAPIBuilder_: CheAPIBuilder,
|
||||
_cheHttpBackend_: CheHttpBackend) => {
|
||||
$rootScope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
cheFactory = _cheFactory_;
|
||||
cheAPIBuilder = _cheAPIBuilder_;
|
||||
cheHttpBackend = _cheHttpBackend_;
|
||||
$httpBackend = _cheHttpBackend_.getHttpBackend();
|
||||
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
});
|
||||
|
||||
describe('validate factory name >', function() {
|
||||
const factoryName1 = 'factoryName1',
|
||||
loadedFactoryName = factoryName1,
|
||||
factoryName2 = 'factoryName2',
|
||||
uniqueName = 'uniqueName',
|
||||
userId = 'userId';
|
||||
|
||||
beforeEach(() => {
|
||||
// setup default user
|
||||
const user = cheAPIBuilder.getUserBuilder().withId(userId).build();
|
||||
cheHttpBackend.setDefaultUser(user);
|
||||
|
||||
cheHttpBackend.usersBackendSetup();
|
||||
|
||||
// setup factories
|
||||
const factoryId1 = 'factoryId1',
|
||||
factory1 = cheAPIBuilder.getFactoryBuilder().withId(factoryId1).withName(factoryName1).build();
|
||||
cheHttpBackend.addUserFactory(factory1);
|
||||
|
||||
const factoryId2 = 'factoryId2',
|
||||
factory2 = cheAPIBuilder.getFactoryBuilder().withId(factoryId2).withName(factoryName2).build();
|
||||
cheHttpBackend.addUserFactory(factory2);
|
||||
|
||||
cheHttpBackend.factoriesBackendSetup();
|
||||
|
||||
cheHttpBackend.setup();
|
||||
|
||||
// setup backend
|
||||
$httpBackend.whenGET( `/api/factory/find?creator.userId=${userId}&name=${uniqueName}`).respond(404, {error: 'not found'});
|
||||
|
||||
// fetch first factory
|
||||
cheFactory.fetchFactoryByName(factoryName1, userId);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
describe('initially factory has an unique name >', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
$rootScope.factoryName = uniqueName;
|
||||
|
||||
const element = angular.element(`
|
||||
<form name="myForm">
|
||||
<input ng-model="factoryName" name="name" unique-factory-name="uniqueFactoryName" />
|
||||
</form>
|
||||
`);
|
||||
$compile(element)($rootScope);
|
||||
myForm = $rootScope.myForm as ITestFormController;
|
||||
});
|
||||
|
||||
it('the form should be valid >', () => {
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
|
||||
// check form (expect valid)
|
||||
expect(myForm.name.$invalid).toBe(false);
|
||||
expect(myForm.name.$valid).toBe(true);
|
||||
});
|
||||
|
||||
it('the HTTP request should be made >', () => {
|
||||
$httpBackend.expectGET(`/api/factory/find?creator.userId=${userId}&name=${uniqueName}`);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
describe('change name to non-unique, factory is already downloaded >', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
myForm.name.$setViewValue(loadedFactoryName);
|
||||
});
|
||||
|
||||
it ('the form should be invalid >', () => {
|
||||
// check form (expect invalid)
|
||||
expect(myForm.name.$invalid).toBe(true);
|
||||
expect(myForm.name.$valid).toBe(false);
|
||||
});
|
||||
|
||||
it('the HTTP request should not be made >', () => {
|
||||
spyOn(cheFactory, 'fetchFactoryByName');
|
||||
expect(cheFactory.fetchFactoryByName).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('change name to non-unique, factory is not downloaded yet >', () => {
|
||||
|
||||
it ('the form should be invalid >', () => {
|
||||
myForm.name.$setViewValue(factoryName2);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
|
||||
// check form (expect invalid)
|
||||
expect(myForm.name.$invalid).toBe(true);
|
||||
expect(myForm.name.$valid).toBe(false);
|
||||
});
|
||||
|
||||
it('the HTTP request should be made >', () => {
|
||||
$httpBackend.expectGET(`/api/factory/find?creator.userId=${userId}&name=${factoryName2}`);
|
||||
|
||||
myForm.name.$setViewValue(factoryName2);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('initially factory has a non-unique name, factory is already downloaded >', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
$rootScope.factoryName = loadedFactoryName;
|
||||
|
||||
const element = angular.element(`
|
||||
<form name="myForm">
|
||||
<input ng-model="factoryName" name="name" unique-factory-name="uniqueFactoryName" />
|
||||
</form>
|
||||
`);
|
||||
$compile(element)($rootScope);
|
||||
myForm = $rootScope.myForm as ITestFormController;
|
||||
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
it('the form should be invalid >', () => {
|
||||
// check form (expect invalid)
|
||||
expect(myForm.name.$invalid).toBe(true);
|
||||
expect(myForm.name.$valid).toBe(false);
|
||||
});
|
||||
|
||||
it('the HTTP request should not be made >', () => {
|
||||
spyOn(cheFactory, 'fetchFactoryByName');
|
||||
expect(cheFactory.fetchFactoryByName).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('change name to unique> ', () => {
|
||||
|
||||
it ('the form should be valid >', () => {
|
||||
myForm.name.$setViewValue(uniqueName);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
|
||||
// check form (expect valid)
|
||||
expect(myForm.name.$invalid).toBe(false);
|
||||
expect(myForm.name.$valid).toBe(true);
|
||||
});
|
||||
|
||||
it('the HTTP request should be made >', () => {
|
||||
$httpBackend.expectGET(`/api/factory/find?creator.userId=${userId}&name=${uniqueName}`);
|
||||
|
||||
myForm.name.$setViewValue(uniqueName);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('initially factory has a non-unique name, factory is not downloaded yet >', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
$rootScope.factoryName = factoryName2;
|
||||
|
||||
const element = angular.element(`
|
||||
<form name="myForm">
|
||||
<input ng-model="factoryName" name="name" unique-factory-name="uniqueFactoryName" />
|
||||
</form>
|
||||
`);
|
||||
$compile(element)($rootScope);
|
||||
myForm = $rootScope.myForm as ITestFormController;
|
||||
});
|
||||
|
||||
it('the form should be invalid >', () => {
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
|
||||
// check form (expect invalid)
|
||||
expect(myForm.name.$invalid).toBe(true);
|
||||
expect(myForm.name.$valid).toBe(false);
|
||||
});
|
||||
|
||||
it('the HTTP request should be made >', () => {
|
||||
$httpBackend.expectGET(`/api/factory/find?creator.userId=${userId}&name=${factoryName2}`);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
describe('change name to unique> ', () => {
|
||||
|
||||
it ('the form should be valid >', () => {
|
||||
myForm.name.$setViewValue(uniqueName);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
|
||||
// check form (expect valid)
|
||||
expect(myForm.name.$invalid).toBe(false);
|
||||
expect(myForm.name.$valid).toBe(true);
|
||||
});
|
||||
|
||||
it('the HTTP request should be made >', () => {
|
||||
$httpBackend.expectGET(`/api/factory/find?creator.userId=${userId}&name=${uniqueName}`);
|
||||
|
||||
myForm.name.$setViewValue(uniqueName);
|
||||
|
||||
// flush HTTP backend
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -18,18 +18,20 @@ import {UniqueStackNameValidator} from './unique-stack-name-validator.directive'
|
|||
import {CityNameValidator} from './city-name-validator.directive';
|
||||
import {CustomAsyncValidator} from './custom-async-validator.directive';
|
||||
import {UniqueTeamNameValidator} from './unique-team-name-validator.directive';
|
||||
import {UniqueFactoryNameValidator} from './unique-factory-name-validator.directive';
|
||||
|
||||
export class ValidatorConfig {
|
||||
|
||||
constructor(register: che.IRegisterService) {
|
||||
|
||||
register.directive('gitUrl', GitUrlValidator)
|
||||
.directive('cityNameValidator', CityNameValidator)
|
||||
.directive('uniqueProjectName', UniqueProjectNameValidator)
|
||||
.directive('uniqueWorkspaceName', UniqueWorkspaceNameValidator)
|
||||
.directive('customValidator', CustomValidator)
|
||||
.directive('customAsyncValidator', CustomAsyncValidator)
|
||||
.directive('uniqueStackName', UniqueStackNameValidator)
|
||||
.directive('uniqueTeamName', UniqueTeamNameValidator);
|
||||
register.directive('gitUrl', GitUrlValidator);
|
||||
register.directive('cityNameValidator', CityNameValidator);
|
||||
register.directive('uniqueProjectName', UniqueProjectNameValidator);
|
||||
register.directive('uniqueWorkspaceName', UniqueWorkspaceNameValidator);
|
||||
register.directive('customValidator', CustomValidator);
|
||||
register.directive('customAsyncValidator', CustomAsyncValidator);
|
||||
register.directive('uniqueStackName', UniqueStackNameValidator);
|
||||
register.directive('uniqueTeamName', UniqueTeamNameValidator);
|
||||
register.directive('uniqueFactoryName', UniqueFactoryNameValidator);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ interface ICheInputAttrs extends ng.IAttributes {
|
|||
cheReadonly: string;
|
||||
cheDisabled: string;
|
||||
cheWidth: string;
|
||||
ngChange: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -53,13 +54,12 @@ export class CheInput implements ng.IDirective {
|
|||
placeHolder: '@chePlaceHolder',
|
||||
pattern: '@chePattern',
|
||||
myForm: '=cheForm',
|
||||
isChanged: '&ngChange',
|
||||
isChanged: '&?ngChange',
|
||||
readonly: '=cheReadonly',
|
||||
disabled: '=cheDisabled'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template for the current toolbar
|
||||
* @param element {ng.IAugmentedJQuery}
|
||||
|
|
@ -85,6 +85,9 @@ export class CheInput implements ng.IDirective {
|
|||
if (attrs.cheDisabled) {
|
||||
template = template + ' ng-disabled="disabled"';
|
||||
}
|
||||
if (attrs.ngChange) {
|
||||
template = template + ' ng-change="isChanged({$value: valueModel})" ';
|
||||
}
|
||||
template = template + ' ng-trim="false" ng-model="valueModel" >'
|
||||
+ '<md-icon class="fa fa-pencil che-input-icon che-input-icon-xs"></md-icon>'
|
||||
+ '<!-- display error messages for the form -->'
|
||||
|
|
@ -107,6 +110,9 @@ export class CheInput implements ng.IDirective {
|
|||
if (attrs.cheDisabled) {
|
||||
template = template + ' ng-disabled="disabled"';
|
||||
}
|
||||
if (attrs.ngChange) {
|
||||
template = template + ' ng-change="isChanged({$value: valueModel})" ';
|
||||
}
|
||||
template = template + '><md-icon class="fa fa-pencil che-input-icon"></md-icon>';
|
||||
if (attrs.cheWidth === 'auto') {
|
||||
template = template + '<div class="che-input-desktop-hidden-text">{{valueModel ? valueModel : placeHolder}}</div>';
|
||||
|
|
@ -191,17 +197,5 @@ export class CheInput implements ng.IDirective {
|
|||
element.removeClass(desktopPristineClass);
|
||||
}
|
||||
});
|
||||
|
||||
const ngChange = 'ngChange';
|
||||
if (!attrs.$attr[ngChange]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// for ngChange attribute only
|
||||
$scope.$watch(() => {
|
||||
return $scope.valueModel;
|
||||
}, () => {
|
||||
$scope.isChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue