Merge pull request #1725 from mabashian/1670-clear-all

Activity stream clear-all bug fixes
This commit is contained in:
Michael Abashian 2018-05-09 12:23:16 -04:00 committed by GitHub
commit 72f982ab55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 145 additions and 205 deletions

View File

@ -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 = "<a href=\"/#/users/" + $scope.activities[i].summary_fields.actor.id + "\">" +
$scope.activities[i].summary_fields.actor.username + "</a>";
} 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;
}
];

View File

@ -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 = "<a href=\"/#/users/" + scope.activities[i].summary_fields.actor.id + "\">" +
scope.activities[i].summary_fields.actor.username + "</a>";
} else {
scope.activities[i].user = 'system';
}
// build description column / action text
BuildDescription(scope.activities[i]);
});
}
};
}
Stream.$inject = ['$rootScope', '$state', 'BuildDescription', 'ShowDetail'];

View File

@ -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)

View File

@ -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;
},

View File

@ -235,7 +235,7 @@ function SmartSearchController (
};
$scope.clearAllTerms = () => {
const cleared = _.cloneDeep(defaults);
const cleared = _(defaults).omit(_.isNull).value();
delete cleared.page;

View File

@ -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: {

View File

@ -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(`<smart-search
django-model="mock"
search-size="mock"
base-path="mock"
iterator="mock"
collection="dataset"
search-tags="searchTags"
>
</smart-search>`);
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(`<smart-search
django-model="mock"
search-size="mock"
base-path="mock"
iterator="mock"
collection="dataset"
search-tags="searchTags"
>
</smart-search>`);
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(`<smart-search
django-model="mock"
search-size="mock"
@ -143,21 +119,14 @@ xdescribe('Directive: Smart Search', () => {
iterator="mock"
collection="dataset"
search-tags="searchTags"
list="list"
>
</smart-search>`);
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', () => {});
});
});

View File

@ -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/';