diff --git a/awx/ui/client/src/activity-stream/activitystream.controller.js b/awx/ui/client/src/activity-stream/activitystream.controller.js
index b7333b0bab..73c8f9c732 100644
--- a/awx/ui/client/src/activity-stream/activitystream.controller.js
+++ b/awx/ui/client/src/activity-stream/activitystream.controller.js
@@ -9,47 +9,69 @@
* @name controllers.function:Activity Stream
* @description This controller controls the activity stream.
*/
-export default ['$scope', '$state', 'subTitle', 'Stream', 'GetTargetTitle',
- 'StreamList', 'Dataset',
- function activityStreamController($scope, $state, subTitle, Stream,
- GetTargetTitle, list, Dataset) {
+export default ['$scope', '$state', 'subTitle', 'GetTargetTitle',
+ 'StreamList', 'Dataset', '$rootScope', 'ShowDetail', 'BuildDescription',
+ function activityStreamController($scope, $state, subTitle, GetTargetTitle,
+ list, Dataset, $rootScope, ShowDetail, BuildDescription) {
- init();
- initOmitSmartTags();
+ // search init
+ $scope.list = list;
+ $scope[`${list.iterator}_dataset`] = Dataset.data;
+ $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- function init() {
- // search init
- $scope.list = list;
- $scope[`${list.iterator}_dataset`] = Dataset.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
+ // subTitle is passed in via a resolve on the route. If there is no subtitle
+ // generated in the resolve then we go get the targets generic title.
- // subTitle is passed in via a resolve on the route. If there is no subtitle
- // generated in the resolve then we go get the targets generic title.
+ // Get the streams sub-title based on the target. This scope variable is leveraged
+ // when we define the activity stream list. Specifically it is included in the list
+ // title.
+ $scope.streamSubTitle = subTitle ? subTitle : GetTargetTitle($state.params.target);
- // Get the streams sub-title based on the target. This scope variable is leveraged
- // when we define the activity stream list. Specifically it is included in the list
- // title.
- $scope.streamSubTitle = subTitle ? subTitle : GetTargetTitle($state.params.target);
+ $rootScope.flashMessage = null;
- // Open the stream
- Stream({
- scope: $scope
+ $scope.refreshStream = function () {
+ $state.go('.', null, {reload: true});
+ };
+
+ $scope.showDetail = function (id) {
+ ShowDetail({
+ scope: $scope,
+ activity_id: id
});
+ };
+
+ if($scope.activities && $scope.activities.length > 0) {
+ buildUserAndDescription();
}
- // Specification of smart-tags omission from the UI is done in the route/state init.
- // A limitation is that this specficiation is static and the key for which to be omitted from
- // the smart-tags must be known at that time.
- // In the case of activity stream, we won't to dynamically ommit the resource for which we are
- // displaying the activity stream for. i.e. 'project', 'credential', etc.
- function initOmitSmartTags() {
- let defaults, route = _.find($state.$current.path, (step) => {
- return step.params.hasOwnProperty('activity_search');
+ $scope.$watch('activities', function(){
+ // Watch for future update to scope.activities (like page change, column sort, search, etc)
+ buildUserAndDescription();
+ });
+
+ function buildUserAndDescription(){
+ $scope.activities.forEach(function(activity, i) {
+ // build activity.user
+ if ($scope.activities[i].summary_fields.actor) {
+ $scope.activities[i].user = "" +
+ $scope.activities[i].summary_fields.actor.username + "";
+ } else {
+ $scope.activities[i].user = 'system';
+ }
+ // build description column / action text
+ BuildDescription($scope.activities[i]);
+
});
- if (route && $state.params.target !== undefined) {
- defaults = route.params.activity_search.config.value;
- defaults[$state.params.target] = null;
- }
}
+
+ const route = _.find($state.$current.path, (step) => {
+ return step.params.hasOwnProperty('activity_search');
+ });
+ let defaultParams = angular.copy(route.params.activity_search.config.value);
+
+ defaultParams.or__object1__in = $state.params.activity_search.or__object1__in ? $state.params.activity_search.or__object1__in : defaultParams.or__object1__in;
+ defaultParams.or__object2__in = $state.params.activity_search.or__object2__in ? $state.params.activity_search.or__object2__in : defaultParams.or__object2__in;
+
+ $scope[`${list.iterator}_default_params`] = defaultParams;
}
];
diff --git a/awx/ui/client/src/activity-stream/factories/stream.factory.js b/awx/ui/client/src/activity-stream/factories/stream.factory.js
deleted file mode 100644
index 43bef1de06..0000000000
--- a/awx/ui/client/src/activity-stream/factories/stream.factory.js
+++ /dev/null
@@ -1,50 +0,0 @@
-export default
- function Stream($rootScope, $state, BuildDescription, ShowDetail) {
- return function (params) {
-
- var scope = params.scope;
-
- $rootScope.flashMessage = null;
-
- // descriptive title describing what AS is showing
- scope.streamTitle = (params && params.title) ? params.title : null;
-
- scope.refreshStream = function () {
- $state.go('.', null, {reload: true});
- };
-
- scope.showDetail = function (id) {
- ShowDetail({
- scope: scope,
- activity_id: id
- });
- };
-
- if(scope.activities && scope.activities.length > 0) {
- buildUserAndDescription();
- }
-
- scope.$watch('activities', function(){
- // Watch for future update to scope.activities (like page change, column sort, search, etc)
- buildUserAndDescription();
- });
-
- function buildUserAndDescription(){
- scope.activities.forEach(function(activity, i) {
- // build activity.user
- if (scope.activities[i].summary_fields.actor) {
- scope.activities[i].user = "" +
- scope.activities[i].summary_fields.actor.username + "";
- } else {
- scope.activities[i].user = 'system';
- }
- // build description column / action text
- BuildDescription(scope.activities[i]);
-
- });
- }
-
- };
- }
-
- Stream.$inject = ['$rootScope', '$state', 'BuildDescription', 'ShowDetail'];
diff --git a/awx/ui/client/src/activity-stream/main.js b/awx/ui/client/src/activity-stream/main.js
index b1e625e2d7..383bab8d11 100644
--- a/awx/ui/client/src/activity-stream/main.js
+++ b/awx/ui/client/src/activity-stream/main.js
@@ -11,7 +11,6 @@ import streamDetailModal from './streamDetailModal/main';
import BuildAnchor from './factories/build-anchor.factory';
import BuildDescription from './factories/build-description.factory';
import ShowDetail from './factories/show-detail.factory';
-import Stream from './factories/stream.factory';
import GetTargetTitle from './get-target-title.factory';
import ModelToBasePathKey from './model-to-base-path-key.factory';
import ActivityDetailForm from './activity-detail.form';
@@ -23,7 +22,6 @@ export default angular.module('activityStream', [streamDetailModal.name])
.factory('BuildAnchor', BuildAnchor)
.factory('BuildDescription', BuildDescription)
.factory('ShowDetail', ShowDetail)
- .factory('Stream', Stream)
.factory('GetTargetTitle', GetTargetTitle)
.factory('ModelToBasePathKey', ModelToBasePathKey)
.factory('ActivityDetailForm', ActivityDetailForm)
diff --git a/awx/ui/client/src/shared/smart-search/queryset.service.js b/awx/ui/client/src/shared/smart-search/queryset.service.js
index ba8aad5dc5..cb0b6087fc 100644
--- a/awx/ui/client/src/shared/smart-search/queryset.service.js
+++ b/awx/ui/client/src/shared/smart-search/queryset.service.js
@@ -34,8 +34,10 @@ function QuerysetService ($q, Rest, ProcessErrors, $rootScope, Wait, DjangoSearc
return defer.promise;
},
replaceDefaultFlags (value) {
- value = value.toString().replace(/__icontains_DEFAULT/g, "__icontains");
- value = value.toString().replace(/__search_DEFAULT/g, "__search");
+ if (value) {
+ value = value.toString().replace(/__icontains_DEFAULT/g, "__icontains");
+ value = value.toString().replace(/__search_DEFAULT/g, "__search");
+ }
return value;
},
diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js
index ba481cec6e..ad14dd5344 100644
--- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js
+++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js
@@ -235,7 +235,7 @@ function SmartSearchController (
};
$scope.clearAllTerms = () => {
- const cleared = _.cloneDeep(defaults);
+ const cleared = _(defaults).omit(_.isNull).value();
delete cleared.page;
diff --git a/awx/ui/test/spec/karma.spec.js b/awx/ui/test/spec/karma.spec.js
index ae2dc8899d..104f8d9ba8 100644
--- a/awx/ui/test/spec/karma.spec.js
+++ b/awx/ui/test/spec/karma.spec.js
@@ -7,24 +7,25 @@ const webpackConfig = require('./webpack.spec');
module.exports = config => {
config.set({
+ basePath: '../..',
autoWatch: true,
colors: true,
browsers: ['Chrome', 'Firefox'],
frameworks: ['jasmine'],
reporters: ['progress', 'junit'],
files:[
- './polyfills.js',
- path.join(SRC_PATH, '**/*.html'),
- path.join(SRC_PATH, 'vendor.js'),
+ 'test/spec/polyfills.js',
+ 'client/src/vendor.js',
path.join(NODE_MODULES, 'angular-mocks/angular-mocks.js'),
path.join(SRC_PATH, 'app.js'),
- '**/*-test.js',
+ 'client/src/**/*.html',
+ 'test/spec/**/*-test.js',
],
preprocessors: {
- [path.join(SRC_PATH, '**/*.html')]: 'html2js',
- [path.join(SRC_PATH, 'vendor.js')]: 'webpack',
+ 'client/src/vendor.js': 'webpack',
[path.join(SRC_PATH, 'app.js')]: 'webpack',
- '**/*-test.js': 'webpack'
+ 'client/src/**/*.html': 'html2js',
+ 'test/spec/**/*-test.js': 'webpack'
},
webpack: webpackConfig,
webpackMiddleware: {
diff --git a/awx/ui/test/spec/smart-search/smart-search.directive-test.js b/awx/ui/test/spec/smart-search/smart-search.directive-test.js
index 59a701e9bc..afc6df33ed 100644
--- a/awx/ui/test/spec/smart-search/smart-search.directive-test.js
+++ b/awx/ui/test/spec/smart-search/smart-search.directive-test.js
@@ -1,7 +1,8 @@
'use strict';
-xdescribe('Directive: Smart Search', () => {
+describe('Directive: Smart Search', () => {
let $scope,
+ $q,
template,
element,
dom,
@@ -9,119 +10,91 @@ xdescribe('Directive: Smart Search', () => {
$state = {},
$stateParams,
GetBasePath,
- QuerySet;
+ QuerySet,
+ ConfigService = {},
+ i18n,
+ $transitions,
+ translateFilter;
beforeEach(angular.mock.module('shared'));
beforeEach(angular.mock.module('SmartSearchModule', ($provide) => {
- QuerySet = jasmine.createSpyObj('QuerySet', ['decodeParam']);
+ QuerySet = jasmine.createSpyObj('QuerySet', [
+ 'decodeParam',
+ 'search',
+ 'stripDefaultParams',
+ 'createSearchTagsFromQueryset',
+ 'initFieldset'
+ ]);
QuerySet.decodeParam.and.callFake((key, value) => {
return `${key.split('__').join(':')}:${value}`;
});
- GetBasePath = jasmine.createSpy('GetBasePath');
+ QuerySet.stripDefaultParams.and.returnValue([]);
+ QuerySet.createSearchTagsFromQueryset.and.returnValue([]);
+ $transitions = jasmine.createSpyObj('$transitions', [
+ 'onSuccess'
+ ]);
+ $transitions.onSuccess.and.returnValue({});
+
+ ConfigService = jasmine.createSpyObj('ConfigService', [
+ 'getConfig'
+ ]);
+
+ GetBasePath = jasmine.createSpy('GetBasePath');
+ translateFilter = jasmine.createSpy('translateFilter');
+ i18n = jasmine.createSpy('i18n');
+ $state = jasmine.createSpyObj('$state', ['go']);
+
+ $provide.value('ConfigService', ConfigService);
$provide.value('QuerySet', QuerySet);
$provide.value('GetBasePath', GetBasePath);
$provide.value('$state', $state);
+ $provide.value('i18n', { '_': (a) => { return a; } });
+ $provide.value('translateFilter', translateFilter);
}));
- beforeEach(angular.mock.inject(($templateCache, _$rootScope_, _$compile_) => {
+ beforeEach(angular.mock.inject(($templateCache, _$rootScope_, _$compile_, _$q_) => {
+ $q = _$q_;
+ $compile = _$compile_;
+ $scope = _$rootScope_.$new();
+
+ ConfigService.getConfig.and.returnValue($q.when({}));
+ QuerySet.search.and.returnValue($q.when({}));
+
+ QuerySet.initFieldset.and.callFake(() => {
+ var deferred = $q.defer();
+ deferred.resolve({
+ models: {
+ mock: {
+ base: {}
+ }
+ },
+ options: {
+ data: null
+ }
+ });
+ return deferred.promise;
+ });
+
// populate $templateCache with directive.templateUrl at test runtime,
template = window.__html__['client/src/shared/smart-search/smart-search.partial.html'];
$templateCache.put('/static/partials/shared/smart-search/smart-search.partial.html', template);
-
- $compile = _$compile_;
- $scope = _$rootScope_.$new();
}));
- describe('initializing tags', () => {
- beforeEach(() => {
- QuerySet.initFieldset = function() {
- return {
- then: function() {
- return;
- }
- };
- };
- });
- // some defaults like page_size and page will always be provided
- // but should be squashed if initialized with default values
- it('should not create tags', () => {
+ describe('clear all', () => {
+ it('should revert search back to non-null defaults and remove page', () => {
$state.$current = {
- params: {
- mock_search: {
- config: {
- value: {
- page_size: '20',
- order_by: '-finished',
- page: '1'
- }
- }
- }
- }
- };
- $state.params = {
- mock_search: {
- page_size: '20',
- order_by: '-finished',
- page: '1'
- }
- };
- dom = angular.element(`
- `);
- element = $compile(dom)($scope);
- $scope.$digest();
- expect($('.SmartSearch-tagContainer', element).length).toEqual(0);
- });
- // set one possible default (order_by) with a custom value, but not another default (page_size)
- it('should create an order_by tag, but not a page_size tag', () => {
- $state.$current = {
- params: {
- mock_search: {
- config: {
- value: {
- page_size: '20',
- order_by: '-finished'
- }
- }
- }
- }
- };
- $state.params = {
- mock_search: {
- page_size: '20',
- order_by: 'name'
- }
- };
- dom = angular.element(`
- `);
- element = $compile(dom)($scope);
- $scope.$digest();
- expect($('.SmartSearch-tagContainer', element).length).toEqual(1);
- expect($('.SmartSearch-tagContainer .SmartSearch-name', element)[0].innerText).toEqual('order_by:name');
- });
- // set many possible defaults and many non-defaults - page_size and page shouldn't generate tags, even when non-default values are set
- it('should create an order_by tag, name tag, description tag - but not a page_size or page tag', () => {
- $state.$current = {
- params: {
- mock_search: {
- config: {
- value: {
- page_size: '20',
- order_by: '-finished',
- page: '1'
+ path: {
+ mock: {
+ params: {
+ mock_search: {
+ config: {
+ value: {
+ page_size: '20',
+ order_by: '-finished',
+ page: '1',
+ some_null_param: null
+ }
+ }
}
}
}
@@ -136,6 +109,9 @@ xdescribe('Directive: Smart Search', () => {
name_icontains: 'ansible'
}
};
+ $scope.list = {
+ iterator: 'mock'
+ };
dom = angular.element(` {
iterator="mock"
collection="dataset"
search-tags="searchTags"
+ list="list"
>
`);
element = $compile(dom)($scope);
$scope.$digest();
- expect($('.SmartSearch-tagContainer', element).length).toEqual(3);
+ const scope = element.isolateScope();
+ scope.clearAllTerms();
+ expect(QuerySet.search).toHaveBeenCalledWith('mock', {page_size: '20',order_by: '-finished',});
});
});
-
- describe('removing tags', () => {
- // assert a default value is still provided after a custom tag is removed
- xit('should revert to state-defined order_by when order_by tag is removed', () => {});
- });
-
- describe('accessing model', () => {
- xit('should retrieve cached model OPTIONS from localStorage', () => {});
- xit('should call QuerySet service to retrieve unstored model OPTIONS', () => {});
- });
});
diff --git a/awx/ui/test/spec/webpack.spec.js b/awx/ui/test/spec/webpack.spec.js
index 6ed0f049ad..099d6a9964 100644
--- a/awx/ui/test/spec/webpack.spec.js
+++ b/awx/ui/test/spec/webpack.spec.js
@@ -1,8 +1,6 @@
-const path = require('path');
-
const webpack = require('webpack');
const merge = require('webpack-merge');
-const base = require(path.resolve(__dirname, '../..', 'build/webpack.base'));
+const base = require('../../build/webpack.base');
const STATIC_URL = '/static/';