Compare commits

..

22 commits

Author SHA1 Message Date
7548aafdb7 maint: move to pingo 2021-01-10 15:39:29 -07:00
0cd3cd96f1 dep updates 2021-01-09 19:36:43 -07:00
22be697ef6
MAINT: swap vanilla sentry for ember-cli-sentry (#92) 2018-04-29 11:58:26 -07:00
bf6023a7b4
BUG: Rollback hasMany changesets sanely (#90) 2018-04-29 07:28:08 -07:00
6f6148df9d
BUG: reverting changeset version bumps (#89)
Fixes #88
2018-04-29 06:55:45 -07:00
437be4fb13
BUG: fix up broken envMeas validations (#86) 2018-04-22 06:48:00 -07:00
9d9e67868f
DEBUG: adding sentry, still need to disable in dev (#85) 2018-04-10 20:42:13 -07:00
86ab62cb0f
DEBUG: console statements for everyone! (#84) 2018-04-10 20:14:48 -07:00
234dc3a34c
BUG: collection measurement date/time field issues (#83) 2018-04-10 06:23:25 -07:00
d2c485af88
BUG: Better date parsing and handling in transform (#82) 2018-03-24 15:59:51 +02:00
ab30692021
BUG: Use datepicker in collection form (#81)
Fixes #73
2018-03-13 07:59:49 -07:00
2ead72c552
ENH: Inline adfg permit and study loc creation (#79)
Fixes #71
2018-03-04 20:34:42 -07:00
467a8d8b64
MAINT: Grab all species records in lookups (#78) 2018-03-03 16:56:44 -07:00
a32d147c1c
BUG: package lock (#77) 2018-03-03 12:45:06 -07:00
c87bd953d9
ENH: Convert sex fields to lookup (#76) 2018-03-03 12:10:12 -07:00
6f01fbf00f
ENH: Add collection delete (#69)
Fixes #37
2018-02-08 08:08:03 -07:00
eb4537afb1
MAINT: Upgrade to ember 2.16 LTS (#66) 2018-01-25 06:51:47 -07:00
39f4789a61
ENH: Collection environment measurement (#65) 2018-01-14 18:01:12 -07:00
56f8796eaf
ENH: Clarify and unify date reprs (#64) 2017-12-12 08:10:56 -07:00
dcbad4d54b
BUG: Editing collections loading indicator (#63) 2017-12-12 07:58:28 -07:00
a4264ac16c
ENH: Add collection notes (#60)
Fixes #32
2017-12-04 21:21:19 -07:00
4dbfcfa98b
ENH: Refactor hasMany relations (#59)
Fixes #52
2017-12-04 05:32:39 -07:00
61 changed files with 22388 additions and 390 deletions

View file

@ -3,11 +3,17 @@ branches:
only:
- master
node_js:
- '6'
sudo: false
- "9"
sudo: required
dist: trusty
addons:
chrome: stable
cache:
directories:
- "$HOME/.npm"
env:
global:
- JOBS=1
before_install:
- npm config set spin false
- npm install -g phantomjs-prebuilt

View file

@ -10,7 +10,7 @@ You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) (with NPM)
* [Ember CLI](https://ember-cli.com/)
* [PhantomJS](http://phantomjs.org/)
* [Google Chrome](https://google.com/chrome/)
## Installation

View file

@ -1,11 +1,9 @@
import Ember from 'ember';
import Application from '@ember/application';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
let App;
App = Ember.Application.extend({
const App = Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver

View file

@ -1,9 +1,11 @@
import Ember from 'ember';
import { Promise } from 'rsvp';
import $ from 'jquery';
import { get } from '@ember/object';
import { isEmpty } from '@ember/utils';
import { run } from '@ember/runloop';
import BaseAuthenticator from 'ember-simple-auth/authenticators/base';
import config from '../config/environment';
const { RSVP: { Promise }, $, get, isEmpty, run } = Ember;
export default BaseAuthenticator.extend({
serverTokenEndpoint: `${config.APP.API_HOST}/api/auth/login/`,
tokenAttributeName: 'data.attributes.auth-token',

View file

@ -1,8 +1,7 @@
import Ember from 'ember';
import { isEmpty } from '@ember/utils';
import { get } from '@ember/object';
import BaseAuthorizer from 'ember-simple-auth/authorizers/base';
const { isEmpty, get } = Ember;
export default BaseAuthorizer.extend({
authorize(data, block) {
const accessToken = get(data, 'data.attributes.auth-token');

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
tagName: 'a',

View file

@ -1,5 +1,3 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({});

View file

@ -1,5 +1,3 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({ });

View file

@ -1,6 +1,6 @@
import Ember from 'ember';
const { Component, computed: { alias }, computed } = Ember;
import Component from '@ember/component';
import { alias } from '@ember/object/computed';
import { computed } from '@ember/object';
export default Component.extend({
// ARGS
@ -8,7 +8,7 @@ export default Component.extend({
// COMPUTED
meta: alias('model.meta'),
links: alias('model.links'),
links: alias('meta.links'),
currentPage: alias('meta.pagination.page'),
totalRecords: alias('meta.pagination.count'),

View file

@ -1,8 +1,6 @@
import Ember from 'ember';
import Component from '@ember/component';
import Table from 'ember-light-table';
const { Component } = Ember;
export default Component.extend({
// ARGS
model: null,

View file

@ -1,8 +1,11 @@
import Ember from 'ember';
import { getProperties, set } from '@ember/object';
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { debounce } from '@ember/runloop';
import RSVP from 'rsvp';
import Changeset from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
const { Component, inject: { service } } = Ember;
import config from 'ccdb-web/config/environment';
export default Component.extend({
store: service(),
@ -11,81 +14,80 @@ export default Component.extend({
this._super(...arguments);
const model = this.get('model');
const validations = this.get('validations');
const hasMany = this.get('hasMany');
let changesets = {};
changesets['new'] = [];
changesets['delete'] = [];
changesets['hasMany'] = [];
changesets['hasMany'] = {};
changesets['model'] = new Changeset(model,
lookupValidator(validations['collection']),
validations['collection']);
let collectionSpeciesChangesets = [];
const collectionSpecies = model.get('collectionSpecies');
collectionSpecies.forEach((cs) => {
const changeset = new Changeset(cs,
lookupValidator(validations['collectionSpecies']),
validations['collectionSpecies']);
collectionSpeciesChangesets.push({ model: cs, changeset: changeset });
hasMany.forEach((hasMany) => {
let relatedChangesets = [];
let validation = validations[hasMany];
const related = model.get(hasMany);
related.forEach((r) => {
const changeset = new Changeset(r, lookupValidator(validation),
validation);
relatedChangesets.push({ model: r, changeset: changeset });
});
changesets['hasMany'][hasMany] = relatedChangesets;
});
changesets['hasMany']['collectionSpecies'] = collectionSpeciesChangesets;
let datasheetsChangesets = [];
const datasheets = model.get('datasheets');
datasheets.forEach((d) => {
const changeset = new Changeset(d,
lookupValidator(validations['datasheet']),
validations['datasheet']);
datasheetsChangesets.push({ model: d, changeset: changeset });
});
changesets['hasMany']['datasheets'] = datasheetsChangesets;
this.set('changesets', changesets);
this.set('newStudyLocationAdmin', `${config.APP.API_HOST}/admin/locations/studylocation/add/`);
},
actions: {
addCollectionSpecies() {
addHasMany(modelName, relatedName) {
const store = this.get('store');
let changesets = this.get('changesets');
const validations = this.get('validations');
const collection = this.get('model');
const cs = store.createRecord('collection-species', { collection: collection });
collection.get('collectionSpecies').pushObject(cs);
changesets['new'].pushObject(cs);
const changeset = new Changeset(cs,
lookupValidator(validations['collectionSpecies']),
validations['collectionSpecies']);
changesets['hasMany']['collectionSpecies'].pushObject({ model: cs, changeset: changeset });
const validation = validations[relatedName];
const model = this.get('model');
const related = store.createRecord(modelName, { collection: model });
model.get(relatedName).pushObject(related);
changesets['new'].pushObject(related);
const changeset = new Changeset(related, lookupValidator(validation), validation);
changesets['hasMany'][relatedName].pushObject({ model: related, changeset: changeset });
},
deleteCollectionSpecies(changesetRecord) {
deleteHasMany(changesetRecord, relatedName) {
let changesets = this.get('changesets');
changesets['delete'].pushObject(changesetRecord.model);
changesets['hasMany']['collectionSpecies'].removeObject(changesetRecord);
changesets['hasMany'][relatedName].removeObject(changesetRecord);
},
// Gross, this side-effects by saving immediately. Someday I should clean
// this up, but for now, you have been warned.
addOption(relatedModelName, optionName, collectionAttrName, relatedAttrName, term) {
const props = getProperties(this, 'store', 'options', 'changesets');
const { store, options, changesets: { model } } = props;
let payload = {};
payload[relatedAttrName] = term;
const record = store.createRecord(relatedModelName, payload)
record.save().then((record) => {
set(options, optionName, store.peekAll(relatedModelName));
set(model, collectionAttrName, record);
});
},
updateDatasheet(changeset, event) {
changeset.set('datasheet', event.target.files[0]);
},
addDatasheet() {
const store = this.get('store');
let changesets = this.get('changesets');
const validations = this.get('validations');
const collection = this.get('model');
const d = store.createRecord('datasheet-attachment', { collection: collection });
collection.get('datasheets').pushObject(d);
changesets['new'].pushObject(d);
const changeset = new Changeset(d,
lookupValidator(validations['datasheets']),
validations['datasheets']);
changesets['hasMany']['datasheets'].pushObject({ model: d, changeset: changeset });
},
deleteDatasheet(changesetRecord) {
let changesets = this.get('changesets');
changesets['delete'].pushObject(changesetRecord.model);
changesets['hasMany']['datasheets'].removeObject(changesetRecord);
searchStudyLocation(term) {
return new RSVP.Promise((resolve, reject) => {
debounce(this, this._performSearch, 'study-location', { page_size: 500, code: term }, resolve, reject, 400);
});
},
},
_performSearch(model, payload, resolve, reject) {
this.get('store').query(model, payload).then((results) => {
resolve(results);
}, reject);
},
});

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
// ARGS
@ -24,6 +22,13 @@ export default Component.extend({
{ label: 'Species', valuePath: 'species.commonName' },
{ label: 'Count', valuePath: 'count' },
{ label: 'Count Estimated?', valuePath: 'countEstimated' },
{ label: 'Sex', valuePath: 'sex' },
{ label: 'Sex', valuePath: 'sex.name' },
],
envMeasColumns: [
{ label: 'Date Measured', valuePath: 'dateMeasured', },
{ label: 'Time Measured', valuePath: 'timeMeasured', },
{ label: 'Water Temp (deg C)', valuePath: 'waterTempC', },
{ label: 'Air Temp (deg C)', valuePath: 'airTempC', },
],
});

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
// ARGS

View file

@ -0,0 +1,23 @@
import Component from '@ember/component';
export default Component.extend({
tagName: 'span',
showConfirm: false,
initialLabel: 'LABEL',
confirmLabel: 'CONFIRM LABEL',
cancelLabel: 'Cancel',
actions: {
initial() {
this.set('showConfirm', true);
},
cancel() {
this.set('showConfirm', false);
},
confirm() {
this.get('onClick')();
},
},
});

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
// ARGS

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
tagName: 'form',

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
const { Component } = Ember;
import Component from '@ember/component';
export default Component.extend({
classNames: ['spinner'],

View file

@ -1,6 +1,6 @@
import Ember from 'ember';
const { Component, computed, get, isEmpty } = Ember;
import Component from '@ember/component';
import { get, computed } from '@ember/object';
import { isEmpty } from '@ember/utils';
export default Component.extend({
classNames: ['form-group'],

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Controller, inject: { service } } = Ember;
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
session: service('session'),

View file

@ -1,21 +1,23 @@
import Ember from 'ember';
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import CollectionValidations from 'ccdb-web/validations/collection';
import CollectionSpeciesValidations from 'ccdb-web/validations/collection-species';
import CollectionMeasurementValidations from 'ccdb-web/validations/collection-measurement';
import DatasheetValidations from 'ccdb-web/validations/datasheet';
import ValidationMixin from 'ccdb-web/mixins/validation';
const { Controller, computed } = Ember;
export default Controller.extend(ValidationMixin, {
CollectionValidations,
CollectionSpeciesValidations,
DatasheetValidations,
CollectionMeasurementValidations,
hasMany: ['collectionSpecies', 'datasheets'],
hasMany: ['collectionSpecies', 'datasheets', 'envMeasurements'],
options: computed('projectOptions', 'studyLocationOptions',
'collectionTypeOptions', 'collectionMethodOptions',
'speciesOptions', 'adfgPermitOptions', function() {
'speciesOptions', 'adfgPermitOptions', 'sexOptions',
function() {
return {
projects: this.get('projectOptions'),
studyLocations: this.get('studyLocationOptions'),
@ -23,17 +25,22 @@ export default Controller.extend(ValidationMixin, {
collectionMethods: this.get('collectionMethodOptions'),
species: this.get('speciesOptions'),
adfgPermits: this.get('adfgPermitOptions'),
sexes: this.get('sexOptions'),
};
}),
actions: {
onSave(changeset) {
const postSave = () => { this.transitionToRoute('collections.index'); };
return this.validationSave(changeset, postSave);
return this.transitionToRoute('loading').then(() => {
return this.validationSave(changeset, postSave);
});
},
onCancel(changeset) {
const postCancel = () => { this.transitionToRoute('collections.index'); };
return this.validationCancel(changeset, postCancel);
return this.transitionToRoute('loading').then(() => {
return this.validationCancel(changeset, postCancel);
});
},
},
});

View file

@ -1,22 +1,23 @@
import Ember from 'ember';
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import CollectionValidations from 'ccdb-web/validations/collection';
import CollectionSpeciesValidations from 'ccdb-web/validations/collection-species';
import CollectionMeasurementValidations from 'ccdb-web/validations/collection-measurement';
import DatasheetValidations from 'ccdb-web/validations/datasheet';
import ValidationMixin from 'ccdb-web/mixins/validation';
const { Controller, computed } = Ember;
export default Controller.extend(ValidationMixin, {
CollectionValidations,
CollectionSpeciesValidations,
DatasheetValidations,
CollectionMeasurementValidations,
hasMany: ['collectionSpecies', 'datasheets'],
safeNavigate: false,
hasMany: ['collectionSpecies', 'datasheets', 'envMeasurements'],
options: computed('projectOptions', 'studyLocationOptions',
'collectionTypeOptions', 'collectionMethodOptions',
'speciesOptions', 'adfgPermitOptions', function() {
'speciesOptions', 'adfgPermitOptions', 'sexOptions',
function() {
return {
projects: this.get('projectOptions'),
studyLocations: this.get('studyLocationOptions'),
@ -24,29 +25,28 @@ export default Controller.extend(ValidationMixin, {
collectionMethods: this.get('collectionMethodOptions'),
species: this.get('speciesOptions'),
adfgPermits: this.get('adfgPermitOptions'),
sexes: this.get('sexOptions'),
};
}),
actions: {
onSave(changesets) {
const postSave = () => {
this.set('safeNavigate', true);
// Use the model's ID here because of the ArrayProxy in the route
this.transitionToRoute('collections.detail', this.get('model.id'));
this.set('safeNavigate', false);
console.log('save');
};
return this.validationSave(changesets, postSave);
return this.transitionToRoute('loading').then(() => {
return this.validationSave(changesets, postSave);
});
},
onCancel(changesets) {
const postCancel = () => {
this.set('safeNavigate', true);
// Use the model's ID here because of the ArrayProxy in the route
this.transitionToRoute('collections.detail', this.get('model.id'));
this.set('safeNavigate', false);
console.log('cancel');
return this.transitionToRoute('collections.detail', this.get('model.id'));
};
return this.validationCancel(changesets, postCancel);
return this.transitionToRoute('loading').then(() => {
return this.validationCancel(changesets, postCancel);
});
},
},
});

View file

@ -1,11 +1,14 @@
import Ember from 'ember';
const { Controller } = Ember;
import Controller from '@ember/controller';
export default Controller.extend({
actions: {
editCollection() {
this.transitionToRoute('collections.detail.edit', this.get('model'));
},
deleteCollection() {
this.get('model')[0].destroyRecord().then(() => {
this.transitionToRoute('collections');
});
},
},
});

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Controller, computed, get, set } = Ember;
import Controller from '@ember/controller';
import { set, get, computed } from '@ember/object';
export default Controller.extend({

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Controller, inject: { service } } = Ember;
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
session: service(),

View file

@ -17,8 +17,8 @@
<body>
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/ccdb-web.js"></script>
<script integrity="" src="{{rootURL}}assets/vendor.js"></script>
<script integrity="" src="{{rootURL}}assets/ccdb-web.js"></script>
{{content-for "body-footer"}}
</body>

View file

@ -1,36 +0,0 @@
import Ember from 'ember';
const { Mixin, run: { once } } = Ember;
export default Mixin.create({
actions: {
willTransition(transition) {
if (!this.get('controller.safeNavigate')) {
if (confirm('Any unsaved changes will be discarded.')) {
let model = this.get('controller.model');
let hasMany = this.get('controller.hasMany');
hasMany.forEach((relationship) => {
model.get(relationship).forEach((r) => {
once(this, () => {
if (r.get('isNew')) {
r.deleteRecord();
} else {
r.rollbackAttributes();
}
}, this);
});
});
if (model.get('isNew')) {
model.deleteRecord();
}
} else {
transition.abort();
return false
}
}
return true;
},
},
});

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Mixin, isArray } = Ember;
import Mixin from '@ember/object/mixin';
import { isArray } from '@ember/array';
const { keys } = Object;
// Portions borrowed from https://github.com/funtusov/ember-cli-form-data

View file

@ -1,8 +1,7 @@
import Ember from 'ember';
const { Mixin, get, RSVP } = Ember;
import Mixin from '@ember/object/mixin';
import { get } from '@ember/object';
import RSVP from 'rsvp';
const { keys } = Object;
const { isArray } = Array;
export default Mixin.create({
validationSave(changesets, postSave) {
@ -55,9 +54,13 @@ export default Mixin.create({
for (const model of changesets[key]) {
model.destroyRecord();
}
} else if (isArray(changesets[key])) { // hasMany
for (const { changeset } of changesets[key]) {
changeset.rollback();
} else if (key === 'hasMany') {
const hasMany = changesets[key];
for (const hasManyKey of keys(changesets[key])) {
const hasManyChangesets = hasMany[hasManyKey];
for (const changeset of hasManyChangesets) {
changeset.rollback();
}
}
} else { // single
const changeset = changesets[key];

View file

@ -0,0 +1,12 @@
import DS from 'ember-data';
const { Model, attr, belongsTo } = DS;
export default Model.extend({
dateMeasured: attr('ccdb-date'),
timeMeasured: attr('string'),
waterTempC: attr('number'),
airTempC: attr('number'),
collection: belongsTo('collection'),
});

View file

@ -3,7 +3,7 @@ import DS from 'ember-data';
const { Model, attr, belongsTo } = DS;
export default Model.extend({
sex: attr('string'),
sex: belongsTo('sex'),
count: attr('number'),
countEstimated: attr('boolean', { defaultValue: false }),

View file

@ -1,16 +1,17 @@
import Ember from 'ember';
import { mapBy } from '@ember/object/computed';
import { computed } from '@ember/object';
import DS from 'ember-data';
const { computed } = Ember;
const { Model, attr, belongsTo, hasMany } = DS;
export default Model.extend({
displayName: attr('string'),
numberOfTraps: attr('number'),
collectionStartDate: attr('string-null-to-empty'),
collectionStartDate: attr('ccdb-date'),
collectionStartTime: attr('string-null-to-empty'),
collectionEndDate: attr('string-null-to-empty'),
collectionEndDate: attr('ccdb-date'),
collectionEndTime: attr('string-null-to-empty'),
notes: attr('string', { defaultValue: '' }),
project: belongsTo('project'),
studyLocation: belongsTo('study-location'),
@ -20,13 +21,14 @@ export default Model.extend({
collectionSpecies: hasMany('collection-species'),
datasheets: hasMany('datasheet-attachment'),
envMeasurements: hasMany('collection-measurement'),
// computed
species: computed.mapBy('collectionSpecies', 'species'),
species: mapBy('collectionSpecies', 'species'),
speciesNames: computed.mapBy('species', 'commonName'),
speciesNames: mapBy('species', 'commonName'),
counts: computed.mapBy('collectionSpecies', 'count'),
counts: mapBy('collectionSpecies', 'count'),
speciesAndCounts: computed('speciesNames', 'counts', function() {
const speciesNames = this.get('speciesNames');

8
app/models/sex.js Normal file
View file

@ -0,0 +1,8 @@
import DS from 'ember-data';
const { Model, attr } = DS;
export default Model.extend({
name: attr('string'),
sortOrder: attr('number'),
});

View file

@ -1,7 +1,7 @@
import Ember from 'ember';
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = Ember.Router.extend({
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
import Route from '@ember/routing/route';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
const { Route } = Ember;
export default Route.extend(ApplicationRouteMixin, {});

View file

@ -1,19 +1,18 @@
import Ember from 'ember';
import CleanupFormMixin from 'ccdb-web/mixins/cleanup-form';
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
const { Route, RSVP } = Ember;
export default Route.extend(CleanupFormMixin, {
export default Route.extend({
model() {
const store = this.get('store');
return RSVP.hash({
model: store.createRecord('collection'),
projectOptions: store.findAll('project'),
studyLocationOptions: store.findAll('study-location'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
collectionTypeOptions: store.findAll('collection-type'),
collectionMethodOptions: store.findAll('collection-method'),
speciesOptions: store.findAll('species'),
speciesOptions: store.query('species', { page_size: 500 }),
adfgPermitOptions: store.findAll('adfg-permit'),
sexOptions: store.findAll('sex'),
});
},

View file

@ -1,12 +1,11 @@
import Ember from 'ember';
const { Route, RSVP } = Ember;
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
export default Route.extend({
model(params) {
return RSVP.all([
this.get('store').findRecord('collection', params.collection_id, {
include: 'collection-species,datasheets',
include: 'collection-species,datasheets,env-measurements',
})
]);
},

View file

@ -1,20 +1,19 @@
import Ember from 'ember';
import CleanupFormMixin from 'ccdb-web/mixins/cleanup-form';
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
const { Route, RSVP } = Ember;
export default Route.extend(CleanupFormMixin, {
export default Route.extend({
model() {
const store = this.get('store');
const model = this.modelFor('collections.detail');
return RSVP.hash({
model: model,
projectOptions: store.findAll('project'),
studyLocationOptions: store.findAll('study-location'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
collectionTypeOptions: store.findAll('collection-type'),
collectionMethodOptions: store.findAll('collection-method'),
speciesOptions: store.findAll('species'),
speciesOptions: store.query('species', { page_size: 500 }),
adfgPermitOptions: store.findAll('adfg-permit'),
sexOptions: store.findAll('sex'),
});
},

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Route, RSVP } = Ember;
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
export default Route.extend({
queryParams: {
@ -30,10 +29,10 @@ export default Route.extend({
projectOptions: store.findAll('project'),
regionOptions: store.findAll('region'),
siteOptions: store.findAll('site'),
studyLocationOptions: store.findAll('study-location'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
collectionMethodOptions: store.findAll('collection-method'),
adfgPermitOptions: store.findAll('adfg-permit'),
speciesOptions: store.findAll('species'),
speciesOptions: store.query('species', { page_size: 500 }),
model: store.query('collection', Object.assign(params, opts)),
});
},
@ -44,27 +43,38 @@ export default Route.extend({
const store = this.get('store');
/* eslint-disable no-console */
let project = controller.get('project');
console.log('project', project);
project = project.map(id => store.peekRecord('project', id));
let region = controller.get('region');
console.log('region', region);
region = region.map(id => store.peekRecord('region', id));
let site = controller.get('site');
console.log('site', site);
site = site.map(id => store.peekRecord('site', id));
let studyLocation = controller.get('study_location');
console.log('studyLocation', studyLocation);
studyLocation = studyLocation.map(id => store.peekRecord('study-location', id));
let collectionMethod = controller.get('collection_method');
console.log('collectionMethod', collectionMethod);
collectionMethod = collectionMethod.map(id => store.peekRecord('collection-method', id));
let adfgPermit = controller.get('adfg_permit');
console.log('adfgPermit', adfgPermit);
adfgPermit = adfgPermit.map(id => store.peekRecord('adfg-permit', id));
let species = controller.get('species');
console.log('species', species);
species = species.map(id => store.peekRecord('species', id));
/* eslint-enable no-console */
const numberOfTraps = controller.get('number_of_traps');
const collectionStartDate = controller.get('collection_start_date');
const collectionEndDate = controller.get('collection_end_date');

View file

@ -1,8 +1,6 @@
import Ember from 'ember';
import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
const { Route } = Ember;
export default Route.extend(AuthenticatedRouteMixin, {
afterModel() {
this.transitionTo('collections');

View file

@ -1,6 +1,4 @@
import Ember from 'ember';
import Route from '@ember/routing/route';
import UnauthenticatedRouteMixin from 'ember-simple-auth/mixins/unauthenticated-route-mixin';
const { Route } = Ember;
export default Route.extend(UnauthenticatedRouteMixin, {});

View file

@ -1,6 +1,5 @@
import Ember from 'ember';
const { Route, inject: { service }} = Ember;
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
session: service('session'),

View file

@ -1,10 +1,16 @@
import Ember from 'ember';
import { capitalize } from '@ember/string';
import DS from 'ember-data';
const { JSONAPISerializer } = DS;
export default JSONAPISerializer.extend({
payloadTypeFromModelName(modelName) {
return modelName.split('-').map(key => Ember.String.capitalize(key)).join('');
}
return modelName.split('-').map(key => capitalize(key)).join('');
},
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
let normalizedDocument = this._super(store, primaryModelClass, payload, id, requestType);
normalizedDocument.meta.links = normalizedDocument.links;
return normalizedDocument;
},
});

View file

@ -4,8 +4,10 @@
validations=(hash
collection=CollectionValidations
collectionSpecies=CollectionSpeciesValidations
envMeasurements=CollectionMeasurementValidations
datasheet=DatasheetValidations)
options=options
hasMany=hasMany
onSave=(action 'onSave')
onCancel=(action 'onCancel')
}}

View file

@ -4,8 +4,10 @@
validations=(hash
collection=CollectionValidations
collectionSpecies=CollectionSpeciesValidations
datasheet=DatasheetValidations)
datasheet=DatasheetValidations
envMeasurements=CollectionMeasurementValidations)
options=options
hasMany=hasMany
onSave=(action 'onSave')
onCancel=(action 'onCancel')
}}

View file

@ -2,4 +2,5 @@
collection/detail-container
model=model
editCollection=(action 'editCollection')
deleteCollection=(action 'deleteCollection')
}}

View file

@ -21,27 +21,33 @@
{{/validated-field}}
{{#validated-field property='adfgPermit' label='ADFG Permit' changeset=changeset}}
{{#power-select
{{#power-select-with-create
options=options.adfgPermits
selected=changeset.adfgPermit
onchange=(action (mut changeset.adfgPermit))
oncreate=(action 'addOption' 'adfg-permit' 'adfgPermits' 'adfgPermit' 'name')
searchField='name'
as |adfgPermit|
as |adfgPermit term|
}}
{{adfgPermit.name}}
{{/power-select}}
{{/power-select-with-create}}
{{/validated-field}}
{{#validated-field property='studyLocation' label='Study location' changeset=changeset}}
{{#power-select
options=options.studyLocations
selected=changeset.studyLocation
onchange=(action (mut changeset.studyLocation))
searchField='name'
as |studyLocation|
}}
{{studyLocation.name}}
{{/power-select}}
{{#validated-field property='studyLocation' changeset=changeset}}
<label class="control-label">
Study location
<a href="{{newStudyLocationAdmin}}" target="_blank">+</a>
</label>
{{#power-select
search=(action 'searchStudyLocation')
options=options.studyLocations
selected=changeset.studyLocation
onchange=(action (mut changeset.studyLocation))
searchField='code'
as |studyLocation|
}}
{{studyLocation.code}}
{{/power-select}}
{{/validated-field}}
{{#validated-field property='collectionType' label='Collection type' changeset=changeset}}
@ -73,114 +79,204 @@
{{/validated-field}}
{{#validated-field property='collectionStartDate' label='Collection start date' changeset=changeset}}
{{input value=changeset.collectionStartDate type='date' class='form-control'}}
{{
pikaday-input
onSelection=(action (mut changeset.collectionStartDate))
value=changeset.collectionStartDate
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{/validated-field}}
{{#validated-field property='collectionStartTime' label='Collection start time' changeset=changeset}}
{{input value=changeset.collectionStartTime type='time' class='form-control'}}
{{input value=changeset.collectionStartTime type='time' class='form-control' placeholder='HH:MM:SS (24 hour)'}}
{{/validated-field}}
{{#validated-field property='collectionEndDate' label='Collection end date' changeset=changeset}}
{{input value=changeset.collectionEndDate type='date' class='form-control'}}
{{
pikaday-input
onSelection=(action (mut changeset.collectionEndDate))
value=changeset.collectionEndDate
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{/validated-field}}
{{#validated-field property='collectionEndTime' label='Collection end time' changeset=changeset}}
{{input value=changeset.collectionEndTime type='time' class='form-control'}}
{{input value=changeset.collectionEndTime type='time' class='form-control' placeholder='HH:MM:SS (24 hour)'}}
{{/validated-field}}
{{/with}}
{{/f.content}}
</div>
</div>
<div class="col-md-8">
<table class="table">
<caption>
Species / Count Info
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addCollectionSpecies')}}
</caption>
<thead>
<tr>
<th class="col-md-3">Species</th>
<th class="col-md-3">Count</th>
<th class="col-md-3">Count Estimated</th>
<th class="col-md-3">Sex</th>
<th class="col-md-1">Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.collectionSpecies as |cs|}}
<tr class="form">
<td class="col-md-3">
{{#validated-field property='species' changeset=cs.changeset}}
{{#power-select
options=options.species
selected=cs.changeset.species
onchange=(action (mut cs.changeset.species))
searchField='commonName'
as |species|
}}
{{species.commonName}}
{{/power-select}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='count' changeset=cs.changeset}}
{{input value=cs.changeset.count}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='countEstimated' changeset=cs.changeset}}
{{input checked=cs.changeset.countEstimated type='checkbox'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='sex' changeset=cs.changeset}}
{{input value=cs.changeset.sex}}
{{/validated-field}}
</td>
<td class="col-md-2">
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteCollectionSpecies' cs)}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form enctype="multipart/form-data">
<table class="table">
<caption>
Attachments
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addDatasheet')}}
</caption>
<thead>
<tr>
<th>File</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.datasheets as |d|}}
<tr class="form">
<td>
{{#if d.model.isNew}}
{{#validated-field property='datasheet' changeset=d.changeset}}
<input type="file" onchange={{action 'updateDatasheet' d.changeset}} accept="image/png,image/jpeg,application/pdf">
{{/validated-field}}
{{else}}
<a href="{{ d.model.datasheet }}">{{ d.model.datasheet }}</a>
{{/if}}
</td>
<td>
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteDatasheet' d)}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</form>
<div class="row">
<div class="col-md-12">
{{#validated-field property='notes' label='Notes' changeset=changesets.model}}
{{textarea value=changesets.model.notes class='form-control'}}
{{/validated-field}}
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table">
<caption>
Species / Count Info
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'collection-species' 'collectionSpecies')}}
</caption>
<thead>
<tr>
<th class="col-md-3">Species</th>
<th class="col-md-3">Count</th>
<th class="col-md-3">Count Estimated</th>
<th class="col-md-3">Sex</th>
<th class="col-md-1">Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.collectionSpecies as |cs|}}
<tr class="form">
<td class="col-md-3">
{{#validated-field property='species' changeset=cs.changeset}}
{{#power-select
options=options.species
selected=cs.changeset.species
onchange=(action (mut cs.changeset.species))
searchField='commonName'
as |species|
}}
{{species.commonName}}
{{/power-select}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='count' changeset=cs.changeset}}
{{input value=cs.changeset.count}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='countEstimated' changeset=cs.changeset}}
{{input checked=cs.changeset.countEstimated type='checkbox'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='sex' changeset=cs.changeset}}
{{#power-select
options=options.sexes
selected=cs.changeset.sex
onchange=(action (mut cs.changeset.sex))
searchField='name'
as |sex|
}}
{{sex.name}}
{{/power-select}}
{{/validated-field}}
</td>
<td class="col-md-2">
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' cs 'collectionSpecies')}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table">
<caption>
Environmental Measurements
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'collection-measurement' 'envMeasurements')}}
</caption>
<thead>
<tr>
<th class="col-md-3">Date Measured</th>
<th class="col-md-3">Time Measured</th>
<th class="col-md-3">Water Temperature (deg C)</th>
<th class="col-md-3">Air Temperature (deg C)</th>
<th class="col-md-1">Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.envMeasurements as |cm|}}
<tr class="form">
<td class="col-md-3">
{{#validated-field property='dateMeasured' changeset=cm.changeset}}
{{
pikaday-input
onSelection=(action (mut cm.changeset.dateMeasured))
value=cm.changeset.dateMeasured
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='timeMeasured' changeset=cm.changeset}}
{{input value=cm.changeset.timeMeasured type='time' class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='waterTempC' changeset=cm.changeset}}
{{input value=cm.changeset.waterTempC class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='airTempC' changeset=cm.changeset}}
{{input value=cm.changeset.airTempC class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-2">
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' cm 'envMeasurements')}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form enctype="multipart/form-data">
<table class="table">
<caption>
Attachments
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'datasheet-attachment' 'datasheets')}}
</caption>
<thead>
<tr>
<th>File</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.datasheets as |d|}}
<tr class="form">
<td>
{{#if d.model.isNew}}
{{#validated-field property='datasheet' changeset=d.changeset}}
<input type="file" onchange={{action 'updateDatasheet' d.changeset}} accept="image/png,image/jpeg,application/pdf">
{{/validated-field}}
{{else}}
<a href="{{ d.model.datasheet }}">{{ d.model.datasheet }}</a>
{{/if}}
</td>
<td>
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' d 'datasheets')}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
{{f.save}} {{f.cancel}}

View file

@ -5,6 +5,13 @@
onClick=(action editCollection)
}}
{{
confirm-button
initialLabel='Delete Collection'
confirmLabel='Yes, Delete Collection'
onClick=(action deleteCollection)
}}
<h3>Main Detail</h3>
<div class="row">
<div class="col-md-12">
@ -33,14 +40,24 @@
<hr>
<h3>Environmental Measurements</h3>
<div class="row">
<div class="col-md-12">
{{#ccdb-table model=model.[0].envMeasurements columns=envMeasColumns as |c|}}
{{#c.grid as |g|}}
{{g.head}}
{{g.body}}
{{/c.grid}}
{{/ccdb-table}}
</div>
</div>
<hr>
<h3>Notes</h3>
<div class="row">
<div class="col-md-12">
<ul>
<li>PLACEHOLDER</li>
<li>PLACEHOLDER</li>
<li>PLACEHOLDER</li>
</ul>
{{model.[0].notes}}
</div>
</div>

View file

@ -102,7 +102,8 @@
onSelection=(action (mut filters.collection_start_date))
value=filters.collection_start_date
useUTC=true
format='YYYY-MM-DD'
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
</div>
@ -113,7 +114,8 @@
onSelection=(action (mut filters.collection_end_date))
value=filters.collection_end_date
useUTC=true
format='YYYY-MM-DD'
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
</div>

View file

@ -0,0 +1,6 @@
{{#if showConfirm}}
{{action-button isDanger=true label=cancelLabel onClick=(action 'cancel')}}
{{action-button isSuccess=true label=confirmLabel onClick=(action 'confirm')}}
{{else}}
{{action-button isDanger=true label=initialLabel onClick=(action 'initial')}}
{{/if}}

View file

@ -0,0 +1,19 @@
import DS from 'ember-data';
export default DS.Transform.extend({
deserialize(serialized) {
return serialized || '';
},
serialize(date) {
if (date !== '') {
date = new Date(date);
const day = date.getUTCDate();
const month = date.getUTCMonth() + 1;
const year = date.getUTCFullYear();
return `${year}-${month}-${day}`;
} else {
return null;
}
}
});

View file

@ -0,0 +1,12 @@
import {
validatePresence,
validateNumber,
} from 'ember-changeset-validations/validators';
export default {
dateMeasured: validatePresence(true),
timeMeasured: validatePresence(true),
waterTempC: validateNumber({ allowBlank: true, integer: false, positive: false }),
airTempC: validateNumber({ allowBlank: true, integer: false, positive: false }),
collection: validatePresence(true),
}

View file

@ -20,13 +20,23 @@ module.exports = function(environment) {
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
sentry: {
dsn: 'https://fd3c695fa9394de48a7c69b7a322960b@sentry.io/1186914',
globalErrorCatching: false,
},
contentSecurityPolicy: {
'script-src': "'self' 'unsafe-inline' 'unsafe-eval'",
'img-src': 'data: app.getsentry.com',
}
};
if (environment === 'development') {
ENV.APP.API_HOST = 'http://localhost:8000';
ENV.APP.API_NAMESPACE = 'api/v1';
ENV.APP.LOG_TRANSITIONS = true;
ENV.sentry.development = true;
}
if (environment === 'test') {
@ -41,7 +51,7 @@ module.exports = function(environment) {
}
if (environment === 'production') {
ENV.APP.API_HOST = 'https://obscure-caverns-99102.herokuapp.com';
ENV.APP.API_HOST = 'https://ccdb-api.thermokar.st';
ENV.APP.API_NAMESPACE = 'api/v1';
}

21802
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -17,25 +17,26 @@
"devDependencies": {
"broccoli-asset-rev": "^2.4.5",
"ember-ajax": "^3.0.0",
"ember-changeset": "1.3.0",
"ember-changeset-validations": "1.2.8",
"ember-cli": "~2.14.0",
"ember-changeset": "^1.3.0",
"ember-changeset-validations": "^1.2.8",
"ember-cli": "^3.24.0",
"ember-cli-app-version": "^3.0.0",
"ember-cli-babel": "^6.3.0",
"ember-cli-babel": "^6.6.0",
"ember-cli-code-coverage": "^0.4.1",
"ember-cli-dependency-checker": "^1.3.0",
"ember-cli-eslint": "^3.0.0",
"ember-cli-flash": "1.4.3",
"ember-cli-dependency-checker": "^2.0.0",
"ember-cli-eslint": "^4.0.0",
"ember-cli-flash": "^1.4.3",
"ember-cli-htmlbars": "^2.0.1",
"ember-cli-htmlbars-inline-precompile": "^0.4.3",
"ember-cli-htmlbars-inline-precompile": "^1.0.0",
"ember-cli-inject-live-reload": "^1.4.1",
"ember-cli-moment-shim": "^3.5.0",
"ember-cli-qunit": "^4.0.0",
"ember-cli-sentry": "^3.0.0",
"ember-cli-shims": "^1.1.0",
"ember-cli-sri": "^2.1.0",
"ember-cli-uglify": "^1.2.0",
"ember-cli-uglify": "^2.0.0",
"ember-composable-helpers": "^2.0.3",
"ember-data": "~2.14.3",
"ember-data": "~2.16.2",
"ember-export-application-global": "^2.0.0",
"ember-inflector": "^2.0.1",
"ember-light-table": "^1.10.0",
@ -43,16 +44,19 @@
"ember-moment": "7.3.1",
"ember-pikaday": "^2.2.3",
"ember-power-select": "^1.8.5",
"ember-power-select-with-create": "0.4.3",
"ember-power-select-with-create": "^0.4.3",
"ember-resolver": "^4.0.0",
"ember-responsive": "^2.0.4",
"ember-simple-auth": "1.4.0",
"ember-sinon": "^1.0.0",
"ember-source": "~2.14.0",
"ember-source": "~2.16.0",
"loader.js": "^4.2.3"
},
"engines": {
"node": "^4.5 || 6.* || >= 7.*"
},
"private": true
"private": true,
"dependencies": {
"npm": "^5.7.1"
}
}

View file

@ -3,10 +3,20 @@ module.exports = {
test_page: 'tests/index.html?hidepassed',
disable_watching: true,
launch_in_ci: [
'PhantomJS'
'Chrome'
],
launch_in_dev: [
'PhantomJS',
'Chrome'
]
],
browser_args: {
Chrome: {
mode: 'ci',
args: [
'--disable-gpu',
'--headless',
'--remote-debugging-port=9222',
'--window-size=1440,900'
]
},
}
};

View file

@ -1,5 +1,5 @@
import Ember from 'ember';
import { run } from '@ember/runloop';
export default function destroyApp(application) {
Ember.run(application, 'destroy');
run(application, 'destroy');
}

View file

@ -1,6 +1,3 @@
import Ember from 'ember';
import FlashObject from 'ember-cli-flash/flash/object';
const { K } = Ember;
FlashObject.reopen({ init: K });
FlashObject.reopen({ init() {} });

View file

@ -1,10 +1,8 @@
import { module } from 'qunit';
import Ember from 'ember';
import { resolve } from 'rsvp';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';
const { RSVP: { resolve } } = Ember;
export default function(name, options = {}) {
module(name, {
beforeEach() {

View file

@ -1,17 +1,16 @@
import Ember from 'ember';
import { registerAsyncHelper } from '@ember/test';
import { A } from '@ember/array';
import { computed } from '@ember/object';
import { classify } from '@ember/string';
import { getOwner } from '@ember/application';
import MediaService from 'ember-responsive/media';
const {
getOwner
} = Ember;
const { classify } = Ember.String;
MediaService.reopen({
// Change this if you want a different default breakpoint in tests.
_defaultBreakpoint: 'desktop',
_breakpointArr: Ember.computed('breakpoints', function() {
return Object.keys(this.get('breakpoints')) || Ember.A([]);
_breakpointArr: computed('breakpoints', function() {
return Object.keys(this.get('breakpoints')) || A([]);
}),
_forceSetBreakpoint(breakpoint) {
@ -45,7 +44,7 @@ MediaService.reopen({
}
});
export default Ember.Test.registerAsyncHelper('setBreakpoint', function(app, breakpoint) {
export default registerAsyncHelper('setBreakpoint', function(app, breakpoint) {
// this should use getOwner once that's supported
const mediaService = app.__deprecatedInstance__.lookup('service:media');
mediaService._forceSetBreakpoint(breakpoint);

View file

@ -1,12 +1,13 @@
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
import { merge } from '@ember/polyfills';
import { run } from '@ember/runloop';
export default function startApp(attrs) {
let attributes = Ember.merge({}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
let attributes = merge({}, config.APP);
attributes = merge(attributes, attrs); // use defaults, but you can override;
return Ember.run(() => {
return run(() => {
let application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();

View file

@ -1,6 +1,6 @@
import $ from 'jquery';
import { moduleFor, test } from 'ember-qunit';
import sinon from 'sinon';
import Ember from 'ember';
moduleFor('authenticator:application', 'Unit | application', {
unit: true,
@ -35,28 +35,28 @@ test('`invalidate` should invalidate the session', function(assert) {
test('`makeRequest` should make a request', function(assert) {
assert.expect(2);
const stub = sinon.stub(Ember.$, 'ajax');
const stub = sinon.stub($, 'ajax');
stub.resolves(42);
const authenticator = this.subject();
authenticator.set('serverTokenEndpoint', 'foo')
const promise = authenticator.makeRequest({bar: 'baz'}).then((d) => {
assert.equal(d, 42);
});
assert.ok(Ember.$.ajax.calledWithMatch({url: 'foo', data: {bar: 'baz'}}));
Ember.$.ajax.restore();
assert.ok($.ajax.calledWithMatch({url: 'foo', data: {bar: 'baz'}}));
$.ajax.restore();
return promise;
});
test('authenticate should craft a nice payload', function(assert) {
assert.expect(2);
const stub = sinon.stub(Ember.$, 'ajax');
const stub = sinon.stub($, 'ajax');
stub.resolves(42);
const authenticator = this.subject();
authenticator.set('serverTokenEndpoint', 'foo')
const promise = authenticator.authenticate('myusername', 'mypassword').then((d) => {
assert.equal(d, 42);
});
assert.ok(Ember.$.ajax.calledWithMatch({url: 'foo', data: {username: 'myusername', password: 'mypassword'}}));
Ember.$.ajax.restore();
assert.ok($.ajax.calledWithMatch({url: 'foo', data: {username: 'myusername', password: 'mypassword'}}));
$.ajax.restore();
return promise;
});