upgrade to Ember 3.28 blueprints (#647)
This commit is contained in:
parent
c48d7d1441
commit
02058ab756
116 changed files with 2128 additions and 1609 deletions
|
@ -14,6 +14,8 @@
|
|||
/api
|
||||
/coverage/
|
||||
!.*
|
||||
.*/
|
||||
.eslintcache
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
|
|
52
.eslintrc.js
52
.eslintrc.js
|
@ -7,15 +7,14 @@ module.exports = {
|
|||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true
|
||||
}
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'ember'
|
||||
],
|
||||
plugins: ['ember'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:ember/recommended'
|
||||
'plugin:ember/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
|
@ -24,6 +23,8 @@ module.exports = {
|
|||
// Croodle is not compliant with some of the recommended rules yet.
|
||||
// We should refactor the code step by step and enable them as soon
|
||||
// as the code is compliant.
|
||||
'ember/classic-decorator-no-classic-methods': 'warn',
|
||||
'ember/no-controller-access-in-routes': 'warn',
|
||||
'ember/no-observers': 'warn',
|
||||
'no-prototype-builtins': 'warn',
|
||||
},
|
||||
|
@ -31,29 +32,40 @@ module.exports = {
|
|||
// node files
|
||||
{
|
||||
files: [
|
||||
'.eslintrc.js',
|
||||
'.template-lintrc.js',
|
||||
'ember-cli-build.js',
|
||||
'testem.js',
|
||||
'blueprints/*/index.js',
|
||||
'config/**/*.js',
|
||||
'lib/*/index.js',
|
||||
'server/**/*.js'
|
||||
'./.eslintrc.js',
|
||||
'./.prettierrc.js',
|
||||
'./.template-lintrc.js',
|
||||
'./ember-cli-build.js',
|
||||
'./testem.js',
|
||||
'./blueprints/*/index.js',
|
||||
'./config/**/*.js',
|
||||
'./lib/*/index.js',
|
||||
'./server/**/*.js',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script'
|
||||
sourceType: 'script',
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true
|
||||
node: true,
|
||||
},
|
||||
plugins: ['node'],
|
||||
extends: ['plugin:node/recommended'],
|
||||
rules: {
|
||||
// this can be removed once the following is fixed
|
||||
// https://github.com/mysticatea/eslint-plugin-node/issues/77
|
||||
'node/no-unpublished-require': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
'node/no-unpublished-require': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
extends: ['plugin:qunit/recommended'],
|
||||
rules: {
|
||||
'qunit/no-assert-logical-expression': 'warn',
|
||||
'qunit/no-async-module-callbacks': 'warn',
|
||||
'qunit/require-expect': 'warn',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@
|
|||
/.env*
|
||||
/.pnp*
|
||||
/.sass-cache
|
||||
/.eslintcache
|
||||
/connect.lock
|
||||
/coverage/
|
||||
/libpeerconnection.log
|
||||
|
|
21
.prettierignore
Normal file
21
.prettierignore
Normal file
|
@ -0,0 +1,21 @@
|
|||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
/vendor/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
/tmp/
|
||||
|
||||
# dependencies
|
||||
/bower_components/
|
||||
/node_modules/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
!.*
|
||||
.eslintcache
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/bower.json.ember-try
|
||||
/package.json.ember-try
|
5
.prettierrc.js
Normal file
5
.prettierrc.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
};
|
|
@ -3,6 +3,7 @@
|
|||
module.exports = {
|
||||
extends: 'recommended',
|
||||
rules: {
|
||||
'no-action': false,
|
||||
'no-implicit-this': {
|
||||
allow: ['scroll-first-invalid-element-into-view-port'],
|
||||
},
|
||||
|
|
|
@ -6,8 +6,7 @@ export default class ApplicationAdapter extends RESTAdapter {
|
|||
encryption;
|
||||
|
||||
// set namespace to api.php in same subdirectory
|
||||
namespace =
|
||||
window.location.pathname
|
||||
namespace = window.location.pathname
|
||||
// remove index.html if it's there
|
||||
.replace(/index.html$/, '')
|
||||
// remove tests prefix which is added by testem (starting with a number)
|
||||
|
@ -19,5 +18,5 @@ export default class ApplicationAdapter extends RESTAdapter {
|
|||
// add api.php
|
||||
.concat('/api/index.php')
|
||||
// remove leading slash
|
||||
.replace(/^\//g, '')
|
||||
.replace(/^\//g, '');
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{{yield}}
|
|
@ -1,8 +1,8 @@
|
|||
import BaseBsForm from "ember-bootstrap/components/bs-form";
|
||||
import IntlMessage from "../utils/intl-message";
|
||||
import BaseBsForm from 'ember-bootstrap/components/bs-form';
|
||||
import IntlMessage from '../utils/intl-message';
|
||||
|
||||
export default class BsForm extends BaseBsForm {
|
||||
"__ember-bootstrap_subclass" = true;
|
||||
'__ember-bootstrap_subclass' = true;
|
||||
|
||||
get hasValidator() {
|
||||
return true;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { isArray } from "@ember/array";
|
||||
import { DateTime } from "luxon";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { isArray } from '@ember/array';
|
||||
import { DateTime } from 'luxon';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class CreateOptionsDates extends Component {
|
||||
@tracked calendarCenter =
|
||||
|
@ -64,21 +64,24 @@ export default class CreateOptionsDates extends Component {
|
|||
});
|
||||
|
||||
if (dateRemoved) {
|
||||
this.args.updateOptions(this.args.options.filter(
|
||||
({ value }) =>
|
||||
DateTime.fromISO(value).toISODate() !== dateRemoved.toISODate()
|
||||
).map(({ value }) => value),
|
||||
this.args.updateOptions(
|
||||
this.args.options
|
||||
.filter(
|
||||
({ value }) =>
|
||||
DateTime.fromISO(value).toISODate() !== dateRemoved.toISODate()
|
||||
)
|
||||
.map(({ value }) => value)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"No date has been added or removed. This cannot be the case. Something spooky is going on."
|
||||
'No date has been added or removed. This cannot be the case. Something spooky is going on.'
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
updateCalenderCenter(diff) {
|
||||
this.calendarCenter = this.calendarCenter.add(diff, "months");
|
||||
this.calendarCenter = this.calendarCenter.add(diff, 'months');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { TrackedArray } from "tracked-built-ins";
|
||||
import { DateTime } from "luxon";
|
||||
import IntlMessage from "../utils/intl-message";
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { TrackedArray } from 'tracked-built-ins';
|
||||
import { DateTime } from 'luxon';
|
||||
import IntlMessage from '../utils/intl-message';
|
||||
|
||||
class FormDataOption {
|
||||
formData;
|
||||
|
@ -23,7 +23,7 @@ class FormDataOption {
|
|||
const { isPartiallyFilled } = this;
|
||||
if (isPartiallyFilled) {
|
||||
return new IntlMessage(
|
||||
"create.options-datetime.error.partiallyFilledTime"
|
||||
'create.options-datetime.error.partiallyFilledTime'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class FormDataOption {
|
|||
.slice(0, optionsForThisDay.indexOf(this))
|
||||
.any((option) => option.time == this.time);
|
||||
if (isDuplicate) {
|
||||
return new IntlMessage("create.options-datetime.error.duplicatedDate");
|
||||
return new IntlMessage('create.options-datetime.error.duplicatedDate');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -73,7 +73,7 @@ class FormData {
|
|||
const { options } = this;
|
||||
const allOptionsAreValid = options.every((option) => option.isValid);
|
||||
if (!allOptionsAreValid) {
|
||||
return IntlMessage("create.options-datetime.error.invalidTime");
|
||||
return IntlMessage('create.options-datetime.error.invalidTime');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -172,7 +172,7 @@ export default class CreateOptionsDatetime extends Component {
|
|||
|
||||
if (!successful) {
|
||||
this.errorMesage =
|
||||
"create.options-datetime.fix-validation-errors-first-day";
|
||||
'create.options-datetime.fix-validation-errors-first-day';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ export default class CreateOptionsDatetime extends Component {
|
|||
|
||||
@action
|
||||
handleTransition(transition) {
|
||||
if (transition.from?.name === "create.options-datetime") {
|
||||
if (transition.from?.name === 'create.options-datetime') {
|
||||
this.args.updateOptions(this.formData.options);
|
||||
this.router.off('routeWillChange', this.handleTransition);
|
||||
}
|
||||
|
@ -216,6 +216,6 @@ export default class CreateOptionsDatetime extends Component {
|
|||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.router.on("routeWillChange", this.handleTransition);
|
||||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { TrackedArray } from "tracked-built-ins";
|
||||
import IntlMessage from "../utils/intl-message";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { TrackedArray } from 'tracked-built-ins';
|
||||
import IntlMessage from '../utils/intl-message';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
class FormDataOption {
|
||||
@tracked value;
|
||||
|
@ -14,7 +14,7 @@ class FormDataOption {
|
|||
|
||||
// Every option must have a label
|
||||
if (!value) {
|
||||
return new IntlMessage("create.options.error.valueMissing");
|
||||
return new IntlMessage('create.options.error.valueMissing');
|
||||
}
|
||||
|
||||
// Options must be unique. There must not be another option having the
|
||||
|
@ -23,7 +23,7 @@ class FormDataOption {
|
|||
.slice(0, this.formData.options.indexOf(this))
|
||||
.some((option) => option.value === this.value);
|
||||
if (!isUnique) {
|
||||
return new IntlMessage("create.options.error.duplicatedOption");
|
||||
return new IntlMessage('create.options.error.duplicatedOption');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -48,11 +48,11 @@ class FormData {
|
|||
if (options.length < 1) {
|
||||
// UI enforces that there is at least one option if poll type is `MakeAPoll`.
|
||||
// This validation error can only happen if poll type is `FindADate`.
|
||||
return new IntlMessage("create.options.error.notEnoughDates");
|
||||
return new IntlMessage('create.options.error.notEnoughDates');
|
||||
}
|
||||
|
||||
if (options.any((option) => !option.isValid)) {
|
||||
return new IntlMessage("create.options.error.invalidOption");
|
||||
return new IntlMessage('create.options.error.invalidOption');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -84,7 +84,7 @@ class FormData {
|
|||
|
||||
// enforce minimal options amount for poll of type MakeAPoll
|
||||
if (this.options.length === 0 && defaultOptionCount > 0) {
|
||||
this.updateOptions(["", ""]);
|
||||
this.updateOptions(['', '']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,15 +109,15 @@ export default class CreateOptionsComponent extends Component {
|
|||
|
||||
@action
|
||||
handleTransition(transition) {
|
||||
if (transition.from?.name === "create.options") {
|
||||
if (transition.from?.name === 'create.options') {
|
||||
this.args.updateOptions(this.formData.options);
|
||||
this.router.off("routeWillChange", this.handleTransition);
|
||||
this.router.off('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.router.on("routeWillChange", this.handleTransition);
|
||||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<select class="language-select" {{on "change" this.handleChange}}>
|
||||
{{! template-lint-disable require-input-label }}
|
||||
<select class='language-select' {{on 'change' this.handleChange}}>
|
||||
{{#each-in this.locales as |localeKey localeName|}}
|
||||
<option value={{localeKey}} selected={{eq localeKey this.currentLocale}}>
|
||||
{{localeName}}
|
||||
|
|
|
@ -19,7 +19,9 @@ export default class LanguageSelect extends Component {
|
|||
handleChange(event) {
|
||||
const locale = event.target.value;
|
||||
|
||||
this.intl.locale = locale.includes('-') ? [locale, locale.split('-')[0]] : [locale];
|
||||
this.intl.locale = locale.includes('-')
|
||||
? [locale, locale.split('-')[0]]
|
||||
: [locale];
|
||||
this.powerCalendar.locale = locale;
|
||||
|
||||
if (window.localStorage) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { DateTime } from "luxon";
|
||||
import Component from '@glimmer/component';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
export default class PollEvaluationParticipantsTable extends Component {
|
||||
get optionsPerDay() {
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class PollEvaluationSummary extends Component {
|
|||
return {
|
||||
answers: copy(answers),
|
||||
option,
|
||||
score: 0
|
||||
score: 0,
|
||||
};
|
||||
});
|
||||
let bestOptions = [];
|
||||
|
@ -56,12 +56,8 @@ export default class PollEvaluationSummary extends Component {
|
|||
|
||||
let bestScore = evaluation[0].score;
|
||||
for (let i = 0; i < evaluation.length; i++) {
|
||||
if (
|
||||
bestScore === evaluation[i].score
|
||||
) {
|
||||
bestOptions.push(
|
||||
evaluation[i]
|
||||
);
|
||||
if (bestScore === evaluation[i].score) {
|
||||
bestOptions.push(evaluation[i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -13,12 +13,18 @@ export default class CreateController extends Controller {
|
|||
|
||||
get canEnterOptionsStep() {
|
||||
let { title } = this.model;
|
||||
return this.visitedSteps.has('options') &&
|
||||
typeof title === 'string' && title.length >= 2;
|
||||
return (
|
||||
this.visitedSteps.has('options') &&
|
||||
typeof title === 'string' &&
|
||||
title.length >= 2
|
||||
);
|
||||
}
|
||||
|
||||
get canEnterOptionsDatetimeStep() {
|
||||
return this.visitedSteps.has('options-datetime') && this.model.options.length >= 1;
|
||||
return (
|
||||
this.visitedSteps.has('options-datetime') &&
|
||||
this.model.options.length >= 1
|
||||
);
|
||||
}
|
||||
|
||||
get canEnterSettingsStep() {
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class CreateIndex extends Controller {
|
||||
@service router;
|
||||
|
||||
@action
|
||||
submit() {
|
||||
this.router.transitionTo("create.meta");
|
||||
this.router.transitionTo('create.meta');
|
||||
}
|
||||
|
||||
@action
|
||||
handleTransition(transition) {
|
||||
if (transition.from?.name === "create.index") {
|
||||
if (transition.from?.name === 'create.index') {
|
||||
const { poll, formData } = this.model;
|
||||
|
||||
poll.pollType = formData.pollType;
|
||||
|
@ -22,6 +22,6 @@ export default class CreateIndex extends Controller {
|
|||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.router.on("routeWillChange", this.handleTransition);
|
||||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import Controller from "@ember/controller";
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import Controller from '@ember/controller';
|
||||
|
||||
export default class CreateMetaController extends Controller {
|
||||
@service router;
|
||||
|
||||
@action
|
||||
previousPage() {
|
||||
this.router.transitionTo("create.index");
|
||||
this.router.transitionTo('create.index');
|
||||
}
|
||||
|
||||
@action
|
||||
submit() {
|
||||
this.router.transitionTo("create.options");
|
||||
this.router.transitionTo('create.options');
|
||||
}
|
||||
|
||||
@action
|
||||
handleTransition(transition) {
|
||||
if (transition.from?.name === "create.meta") {
|
||||
if (transition.from?.name === 'create.meta') {
|
||||
const { poll, formData } = this.model;
|
||||
|
||||
poll.title = formData.title;
|
||||
|
@ -28,6 +28,6 @@ export default class CreateMetaController extends Controller {
|
|||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.router.on("routeWillChange", this.handleTransition);
|
||||
this.router.on('routeWillChange', this.handleTransition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import Controller from '@ember/controller';
|
||||
import { action } from '@ember/object';
|
||||
import { DateTime } from 'luxon';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
|
@ -8,21 +8,23 @@ export default class CreateOptionsDatetimeController extends Controller {
|
|||
|
||||
@action
|
||||
nextPage() {
|
||||
this.router.transitionTo("create.settings");
|
||||
this.router.transitionTo('create.settings');
|
||||
}
|
||||
|
||||
@action
|
||||
previousPage() {
|
||||
this.router.transitionTo("create.options");
|
||||
this.router.transitionTo('create.options');
|
||||
}
|
||||
|
||||
@action
|
||||
updateOptions(options) {
|
||||
this.model.options = options
|
||||
.map(({ day, time }) => time ? DateTime.fromISO(`${day}T${time}`).toISO() : day)
|
||||
.map(({ day, time }) =>
|
||||
time ? DateTime.fromISO(`${day}T${time}`).toISO() : day
|
||||
)
|
||||
.sort()
|
||||
.map((isoString) => {
|
||||
return this.store.createFragment("option", { title: isoString });
|
||||
return this.store.createFragment('option', { title: isoString });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class CreateOptionsController extends Controller {
|
||||
@service router;
|
||||
|
@ -10,21 +10,21 @@ export default class CreateOptionsController extends Controller {
|
|||
const { isFindADate } = this.model;
|
||||
|
||||
if (isFindADate) {
|
||||
this.router.transitionTo("create.options-datetime");
|
||||
this.router.transitionTo('create.options-datetime');
|
||||
} else {
|
||||
this.router.transitionTo("create.settings");
|
||||
this.router.transitionTo('create.settings');
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
previousPage() {
|
||||
this.router.transitionTo("create.meta");
|
||||
this.router.transitionTo('create.meta');
|
||||
}
|
||||
|
||||
@action
|
||||
updateOptions(newOptions) {
|
||||
this.model.options = newOptions.map(({ value }) =>
|
||||
this.store.createFragment("option", { title: value })
|
||||
this.store.createFragment('option', { title: value })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,16 +39,31 @@ export default class CreateSettings extends Controller {
|
|||
set expirationDuration(value) {
|
||||
this.model.expirationDate = isPresent(value)
|
||||
? DateTime.local().plus(Duration.fromISO(value)).toISO()
|
||||
: "";
|
||||
: '';
|
||||
}
|
||||
|
||||
get expirationDurations() {
|
||||
return [
|
||||
{ id: 'P7D', labelTranslation: 'create.settings.expirationDurations.P7D' },
|
||||
{ id: 'P1M', labelTranslation: 'create.settings.expirationDurations.P1M' },
|
||||
{ id: 'P3M', labelTranslation: 'create.settings.expirationDurations.P3M' },
|
||||
{ id: 'P6M', labelTranslation: 'create.settings.expirationDurations.P6M' },
|
||||
{ id: 'P1Y', labelTranslation: 'create.settings.expirationDurations.P1Y' },
|
||||
{
|
||||
id: 'P7D',
|
||||
labelTranslation: 'create.settings.expirationDurations.P7D',
|
||||
},
|
||||
{
|
||||
id: 'P1M',
|
||||
labelTranslation: 'create.settings.expirationDurations.P1M',
|
||||
},
|
||||
{
|
||||
id: 'P3M',
|
||||
labelTranslation: 'create.settings.expirationDurations.P3M',
|
||||
},
|
||||
{
|
||||
id: 'P6M',
|
||||
labelTranslation: 'create.settings.expirationDurations.P6M',
|
||||
},
|
||||
{
|
||||
id: 'P1Y',
|
||||
labelTranslation: 'create.settings.expirationDurations.P1Y',
|
||||
},
|
||||
{ id: '', labelTranslation: 'create.settings.expirationDurations.never' },
|
||||
];
|
||||
}
|
||||
|
@ -82,13 +97,16 @@ export default class CreateSettings extends Controller {
|
|||
return option.hasTime;
|
||||
})
|
||||
) {
|
||||
this.set('model.timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||
this.set(
|
||||
'model.timezone',
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
);
|
||||
}
|
||||
|
||||
// save poll
|
||||
try {
|
||||
await poll.save();
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
this.flashMessages.danger('error.poll.savingFailed');
|
||||
|
||||
throw err;
|
||||
|
|
|
@ -34,7 +34,9 @@ export default class PollController extends Controller {
|
|||
if (isEmpty(expirationDate)) {
|
||||
return false;
|
||||
}
|
||||
return DateTime.local().plus({ weeks: 2 }) >= DateTime.fromISO(expirationDate);
|
||||
return (
|
||||
DateTime.local().plus({ weeks: 2 }) >= DateTime.fromISO(expirationDate)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -44,7 +46,10 @@ export default class PollController extends Controller {
|
|||
const { model: poll } = this;
|
||||
const { timezone: pollTimezone } = poll;
|
||||
|
||||
return isPresent(pollTimezone) && Intl.DateTimeFormat().resolvedOptions().timeZone !== pollTimezone;
|
||||
return (
|
||||
isPresent(pollTimezone) &&
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone !== pollTimezone
|
||||
);
|
||||
}
|
||||
|
||||
get mustChooseTimezone() {
|
||||
|
@ -85,7 +90,10 @@ export default class PollController extends Controller {
|
|||
this.encryptionKey !== this.encryption.key
|
||||
) {
|
||||
// work-a-round for url not being updated
|
||||
window.location.hash = window.location.hash.replace(this.encryptionKey, this.encryption.key);
|
||||
window.location.hash = window.location.hash.replace(
|
||||
this.encryptionKey,
|
||||
this.encryption.key
|
||||
);
|
||||
|
||||
this.set('encryptionKey', this.encryption.key);
|
||||
}
|
||||
|
|
|
@ -53,14 +53,14 @@ export default class PollEvaluationController extends Controller {
|
|||
}
|
||||
|
||||
// create lookup array
|
||||
evaluation.forEach(function(value, index) {
|
||||
evaluation.forEach(function (value, index) {
|
||||
lookup[value.id] = index;
|
||||
});
|
||||
|
||||
// loop over all users
|
||||
poll.users.forEach((user) => {
|
||||
// loop over all selections of the user
|
||||
user.selections.forEach(function(selection, optionIndex) {
|
||||
user.selections.forEach(function (selection, optionIndex) {
|
||||
let answerIndex;
|
||||
|
||||
// get answer index by lookup array
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import config from "croodle/config/environment";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Controller, { inject as controller } from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import config from 'croodle/config/environment';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class PollParticipationController extends Controller {
|
||||
@service encryption;
|
||||
@service router;
|
||||
|
||||
@controller("poll")
|
||||
@controller('poll')
|
||||
pollController;
|
||||
|
||||
@tracked name = "";
|
||||
@tracked name = '';
|
||||
@tracked savingFailed = false;
|
||||
|
||||
@action
|
||||
|
@ -31,7 +31,7 @@ export default class PollParticipationController extends Controller {
|
|||
}
|
||||
|
||||
// map selection to answer if it's not freetext
|
||||
let answer = answers.findBy("type", value);
|
||||
let answer = answers.findBy('type', value);
|
||||
let { icon, label, labelTranslation, type } = answer;
|
||||
|
||||
return {
|
||||
|
@ -42,7 +42,7 @@ export default class PollParticipationController extends Controller {
|
|||
};
|
||||
});
|
||||
|
||||
let user = this.store.createRecord("user", {
|
||||
let user = this.store.createRecord('user', {
|
||||
creationDate: new Date(),
|
||||
name,
|
||||
poll,
|
||||
|
@ -70,7 +70,7 @@ export default class PollParticipationController extends Controller {
|
|||
return;
|
||||
}
|
||||
|
||||
this.router.transitionTo("poll.evaluation", poll.id, {
|
||||
this.router.transitionTo('poll.evaluation', poll.id, {
|
||||
queryParams: { encryptionKey: this.encryption.key },
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
export default {
|
||||
};
|
||||
export default {};
|
||||
|
|
|
@ -2,7 +2,6 @@ import Helper from '@ember/component/helper';
|
|||
import { DateTime } from 'luxon';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
|
||||
export default class FormatDateRelativeHelper extends Helper {
|
||||
@service intl;
|
||||
|
||||
|
|
|
@ -7,24 +7,29 @@ function elementIsNotVisible(element) {
|
|||
let windowHeight = window.innerHeight;
|
||||
|
||||
// an element is not visible if
|
||||
return false ||
|
||||
// it's above the current view port
|
||||
elementPosition.top <= 0 ||
|
||||
// it's below the current view port
|
||||
elementPosition.bottom >= windowHeight ||
|
||||
// it's in current view port but hidden by fixed navigation
|
||||
(
|
||||
getComputedStyle(document.querySelector('.cr-steps-bottom-nav')).position === 'fixed' &&
|
||||
elementPosition.bottom >= windowHeight - document.querySelector('.cr-steps-bottom-nav').offsetHeight
|
||||
);
|
||||
return (
|
||||
false ||
|
||||
// it's above the current view port
|
||||
elementPosition.top <= 0 ||
|
||||
// it's below the current view port
|
||||
elementPosition.bottom >= windowHeight ||
|
||||
// it's in current view port but hidden by fixed navigation
|
||||
(getComputedStyle(document.querySelector('.cr-steps-bottom-nav'))
|
||||
.position === 'fixed' &&
|
||||
elementPosition.bottom >=
|
||||
windowHeight -
|
||||
document.querySelector('.cr-steps-bottom-nav').offsetHeight)
|
||||
);
|
||||
}
|
||||
|
||||
export function scrollFirstInvalidElementIntoViewPort() {
|
||||
// `schedule('afterRender', function() {})` would be more approperiate but there seems to be a
|
||||
// timing issue in Firefox causing the Browser not scrolling up far enough if doing so
|
||||
// delaying to next runloop therefore
|
||||
next(function() {
|
||||
let invalidInput = document.querySelector('.form-control.is-invalid, .custom-control-input.is-invalid');
|
||||
next(function () {
|
||||
let invalidInput = document.querySelector(
|
||||
'.form-control.is-invalid, .custom-control-input.is-invalid'
|
||||
);
|
||||
assert(
|
||||
'Atleast one form control must be marked as invalid if form submission was rejected as invalid',
|
||||
invalidInput
|
||||
|
@ -42,7 +47,12 @@ export function scrollFirstInvalidElementIntoViewPort() {
|
|||
// As a work-a-round we look the correct label up by a custom convention for the `id` of the
|
||||
// inputs and the `for` of the input group `<label>` (which should be a `<legend>`).
|
||||
let scrollTarget =
|
||||
document.querySelector(`label[for="${invalidInput.id.substr(0, invalidInput.id.indexOf('_'))}"`) ||
|
||||
document.querySelector(
|
||||
`label[for="${invalidInput.id.substr(
|
||||
0,
|
||||
invalidInput.id.indexOf('_')
|
||||
)}"`
|
||||
) ||
|
||||
document.querySelector(`label[for="${invalidInput.id}"]`) ||
|
||||
// For polls with type `MakeAPoll` the option inputs do not have a label at all. In that case
|
||||
// we scroll to the input element itself
|
||||
|
@ -53,6 +63,6 @@ export function scrollFirstInvalidElementIntoViewPort() {
|
|||
});
|
||||
}
|
||||
|
||||
export default helper(function() {
|
||||
export default helper(function () {
|
||||
return scrollFirstInvalidElementIntoViewPort;
|
||||
});
|
||||
|
|
|
@ -10,16 +10,16 @@ export default {
|
|||
let availableLocales = Object.keys(localesMeta);
|
||||
let locale = getLocale(availableLocales);
|
||||
|
||||
intl.set('locale', locale.includes('-') ? [locale, locale.split('-')[0]] : [locale]);
|
||||
intl.set(
|
||||
'locale',
|
||||
locale.includes('-') ? [locale, locale.split('-')[0]] : [locale]
|
||||
);
|
||||
powerCalendar.set('local', locale);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function getLocale(availableLocales) {
|
||||
let methods = [
|
||||
getSavedLocale,
|
||||
getLocaleByBrowser
|
||||
];
|
||||
let methods = [getSavedLocale, getLocaleByBrowser];
|
||||
let locale;
|
||||
|
||||
methods.any((method) => {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export default {
|
||||
'ca': 'catalan',
|
||||
'de': 'deutsch',
|
||||
'en': 'english',
|
||||
ca: 'catalan',
|
||||
de: 'deutsch',
|
||||
en: 'english',
|
||||
'en-gb': 'english (GB)',
|
||||
'es': 'español',
|
||||
'fr': 'français',
|
||||
'it': 'italiano',
|
||||
'nb': 'norsk',
|
||||
es: 'español',
|
||||
fr: 'français',
|
||||
it: 'italiano',
|
||||
nb: 'norsk',
|
||||
};
|
||||
|
|
|
@ -40,8 +40,7 @@ export default class Option extends Fragment {
|
|||
}
|
||||
|
||||
get hasTime() {
|
||||
return this.isDate &&
|
||||
this.title.length >= 'YYYY-MM-DDTHH:mm'.length;
|
||||
return this.isDate && this.title.length >= 'YYYY-MM-DDTHH:mm'.length;
|
||||
}
|
||||
|
||||
get time() {
|
||||
|
@ -67,6 +66,11 @@ export default class Option extends Fragment {
|
|||
if (!datetime.isValid) {
|
||||
return;
|
||||
}
|
||||
this.set('title', this.datetime.set({ hours: datetime.hour, minutes: datetime.minute }).toISO());
|
||||
this.set(
|
||||
'title',
|
||||
this.datetime
|
||||
.set({ hours: datetime.hour, minutes: datetime.minute })
|
||||
.toISO()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ export default class Poll extends Model {
|
|||
|
||||
// polls description
|
||||
@attr('string', {
|
||||
defaultValue: ''
|
||||
defaultValue: '',
|
||||
})
|
||||
description;
|
||||
|
||||
// ISO 8601 date + time string in UTC
|
||||
@attr('string', {
|
||||
includePlainOnCreate: 'serverExpirationDate'
|
||||
includePlainOnCreate: 'serverExpirationDate',
|
||||
})
|
||||
expirationDate;
|
||||
|
||||
|
@ -61,7 +61,7 @@ export default class Poll extends Model {
|
|||
|
||||
// Croodle version poll got created with
|
||||
@attr('string', {
|
||||
encrypted: false
|
||||
encrypted: false,
|
||||
})
|
||||
version;
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import classic from 'ember-classic-decorator';
|
||||
import Model, { belongsTo, attr } from '@ember-data/model';
|
||||
import {
|
||||
fragmentArray
|
||||
} from 'ember-data-model-fragments/attributes';
|
||||
import { fragmentArray } from 'ember-data-model-fragments/attributes';
|
||||
|
||||
@classic
|
||||
export default class User extends Model {
|
||||
|
@ -30,7 +28,7 @@ export default class User extends Model {
|
|||
|
||||
// Croodle version user got created with
|
||||
@attr('string', {
|
||||
encrypted: false
|
||||
encrypted: false,
|
||||
})
|
||||
version;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { modifier } from 'ember-modifier';
|
||||
|
||||
export default modifier(function autofocus(element, params, { enabled = true }) {
|
||||
export default modifier(function autofocus(
|
||||
element,
|
||||
params,
|
||||
{ enabled = true }
|
||||
) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ export default class Router extends EmberRouter {
|
|||
rootURL = config.rootURL;
|
||||
}
|
||||
|
||||
Router.map(function() {
|
||||
this.route('poll', { path: '/poll/:poll_id' }, function() {
|
||||
Router.map(function () {
|
||||
this.route('poll', { path: '/poll/:poll_id' }, function () {
|
||||
this.route('participation');
|
||||
this.route('evaluation');
|
||||
});
|
||||
this.route('create', function() {
|
||||
this.route('create', function () {
|
||||
this.route('meta');
|
||||
this.route('options');
|
||||
this.route('options-datetime');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Route from '@ember/routing/route';
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
class FormData {
|
||||
@tracked pollType;
|
||||
|
|
|
@ -10,7 +10,9 @@ class FormData {
|
|||
const { title } = this;
|
||||
|
||||
if (!title) {
|
||||
return new IntlMessage('create.meta.input.title.validations.valueMissing');
|
||||
return new IntlMessage(
|
||||
'create.meta.input.title.validations.valueMissing'
|
||||
);
|
||||
}
|
||||
|
||||
if (title.length < 2) {
|
||||
|
|
|
@ -27,8 +27,8 @@ export default class PollRoute extends Route {
|
|||
if (transition.targetName === 'poll.index') {
|
||||
this.transitionTo('poll.participation', poll, {
|
||||
queryParams: {
|
||||
encryptionKey: this.encryption.key
|
||||
}
|
||||
encryptionKey: this.encryption.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Route from "@ember/routing/route";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { TrackedArray } from "tracked-built-ins";
|
||||
import IntlMessage from "../../utils/intl-message";
|
||||
import Route from '@ember/routing/route';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { TrackedArray } from 'tracked-built-ins';
|
||||
import IntlMessage from '../../utils/intl-message';
|
||||
|
||||
class FormDataSelections {
|
||||
@tracked value = null;
|
||||
|
@ -11,7 +11,7 @@ class FormDataSelections {
|
|||
const { value, valueIsRequired } = this;
|
||||
|
||||
if (!value && valueIsRequired) {
|
||||
return new IntlMessage("poll.error.selection.valueMissing");
|
||||
return new IntlMessage('poll.error.selection.valueMissing');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -36,12 +36,12 @@ class FormData {
|
|||
const { name, nameIsRequired, namesTaken } = this;
|
||||
|
||||
if (!name && nameIsRequired) {
|
||||
return new IntlMessage("poll.error.name.valueMissing");
|
||||
return new IntlMessage('poll.error.name.valueMissing');
|
||||
}
|
||||
|
||||
// TODO: Validate that name is unique for this poll
|
||||
if (namesTaken.includes(name)) {
|
||||
return new IntlMessage("poll.error.name.duplicate");
|
||||
return new IntlMessage('poll.error.name.duplicate');
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -68,7 +68,7 @@ class FormData {
|
|||
|
||||
export default class ParticipationRoute extends Route {
|
||||
model() {
|
||||
const poll = this.modelFor("poll");
|
||||
const poll = this.modelFor('poll');
|
||||
const { anonymousUser, forceAnswer, options, users } = poll;
|
||||
const formData = new FormData(options, {
|
||||
nameIsRequired: !anonymousUser,
|
||||
|
|
|
@ -27,13 +27,13 @@ export default class ApplicationSerializer extends RESTSerializer {
|
|||
* implement decryption
|
||||
*/
|
||||
normalize(modelClass, resourceHash, prop) {
|
||||
|
||||
// run before serialization of attribute hash
|
||||
modelClass.eachAttribute(function(key, attributes) {
|
||||
if (
|
||||
attributes.options.encrypted !== false
|
||||
) {
|
||||
if (typeof resourceHash[key] !== 'undefined' && resourceHash[key] !== null) {
|
||||
modelClass.eachAttribute(function (key, attributes) {
|
||||
if (attributes.options.encrypted !== false) {
|
||||
if (
|
||||
typeof resourceHash[key] !== 'undefined' &&
|
||||
resourceHash[key] !== null
|
||||
) {
|
||||
resourceHash[key] = this.encryption.decrypt(resourceHash[key]);
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,7 @@ export default class ApplicationSerializer extends RESTSerializer {
|
|||
}
|
||||
|
||||
// encrypt after serialization of attribute hash
|
||||
if (
|
||||
attribute.options.encrypted !== false
|
||||
) {
|
||||
if (attribute.options.encrypted !== false) {
|
||||
json[key] = this.encryption.encrypt(json[key]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@ import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
|
|||
import { isEmpty } from '@ember/utils';
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default class PollSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
|
||||
export default class PollSerializer extends ApplicationSerializer.extend(
|
||||
EmbeddedRecordsMixin
|
||||
) {
|
||||
attrs = {
|
||||
users: {
|
||||
deserialize: 'records'
|
||||
}
|
||||
deserialize: 'records',
|
||||
},
|
||||
};
|
||||
|
||||
legacySupport(resourceHash) {
|
||||
|
|
|
@ -14,17 +14,17 @@ export default class UserSerializer extends ApplicationSerializer {
|
|||
* and selection property "type" where named "id"
|
||||
*/
|
||||
if (!isEmpty(resourceHash.selections[0].value)) {
|
||||
resourceHash.selections.forEach(function(selection, index) {
|
||||
resourceHash.selections.forEach(function (selection, index) {
|
||||
if (typeof selection.value === 'string') {
|
||||
resourceHash.selections[index] = {
|
||||
label: selection.value
|
||||
label: selection.value,
|
||||
};
|
||||
} else {
|
||||
resourceHash.selections[index] = {
|
||||
icon: selection.value.icon,
|
||||
label: selection.value.label,
|
||||
labelTranslation: selection.value.labelTranslation,
|
||||
type: selection.value.id
|
||||
type: selection.value.id,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,19 +8,11 @@ export default class EncryptionService extends Service {
|
|||
key = null;
|
||||
|
||||
decrypt(value) {
|
||||
return JSON.parse(
|
||||
sjcl.decrypt(
|
||||
this.key,
|
||||
value
|
||||
)
|
||||
);
|
||||
return JSON.parse(sjcl.decrypt(this.key, value));
|
||||
}
|
||||
|
||||
encrypt(value) {
|
||||
return sjcl.encrypt(
|
||||
this.key,
|
||||
JSON.stringify(value)
|
||||
);
|
||||
return sjcl.encrypt(this.key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
generateKey() {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<main role="main" class="container cr-main">
|
||||
<main class="container cr-main">
|
||||
<div id="messages">
|
||||
{{#each this.flashMessages.queue as |flash|}}
|
||||
<FlashMessage @flash={{flash}}>
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
{{#let @model as |poll|}}
|
||||
{{page-title poll.title}}
|
||||
|
||||
<div id="poll">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-lg-5">
|
||||
<div class="box meta-data">
|
||||
<h2 class="title">{{poll.title}}</h2>
|
||||
<p class="description">{{poll.description}}</p>
|
||||
<p class="dates">
|
||||
<span class="creationDate">
|
||||
<div id='poll'>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6 col-lg-5'>
|
||||
<div class='box meta-data'>
|
||||
<h2 class='title'>{{poll.title}}</h2>
|
||||
<p class='description'>{{poll.description}}</p>
|
||||
<p class='dates'>
|
||||
<span class='creationDate'>
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" and timeStyle="short" after upgrading to Ember Intl v6
|
||||
}}
|
||||
{{t
|
||||
"poll.created-date"
|
||||
'poll.created-date'
|
||||
date=(format-date
|
||||
poll.creationDate
|
||||
weekday="long"
|
||||
day="numeric"
|
||||
month="long"
|
||||
year="numeric"
|
||||
hour="numeric"
|
||||
minute="numeric"
|
||||
weekday='long'
|
||||
day='numeric'
|
||||
month='long'
|
||||
year='numeric'
|
||||
hour='numeric'
|
||||
minute='numeric'
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
{{#if poll.expirationDate}}
|
||||
<br />
|
||||
<span class="expirationDate">
|
||||
<span class='expirationDate'>
|
||||
{{!
|
||||
TODO: Simplify to dateStyle="full" and timeStyle="short" after upgrading to Ember Intl v6
|
||||
}}
|
||||
{{t
|
||||
"poll.expiration-date"
|
||||
'poll.expiration-date'
|
||||
date=(format-date
|
||||
poll.expirationDate
|
||||
weekday="long"
|
||||
day="numeric"
|
||||
month="long"
|
||||
year="numeric"
|
||||
hour="numeric"
|
||||
minute="numeric"
|
||||
weekday='long'
|
||||
day='numeric'
|
||||
month='long'
|
||||
year='numeric'
|
||||
hour='numeric'
|
||||
minute='numeric'
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
|
@ -48,40 +48,40 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-6 offset-lg-1">
|
||||
<div class="box poll-link cr-poll-link">
|
||||
<p>{{t "poll.share.title"}}</p>
|
||||
<p class="link cr-poll-link__link">
|
||||
<div class='col-sm-6 col-lg-6 offset-lg-1'>
|
||||
<div class='box poll-link cr-poll-link'>
|
||||
<p>{{t 'poll.share.title'}}</p>
|
||||
<p class='link cr-poll-link__link'>
|
||||
<small>
|
||||
<code class="cr-poll-link__url">{{this.pollUrl}}</code>
|
||||
<code class='cr-poll-link__url'>{{this.pollUrl}}</code>
|
||||
</small>
|
||||
<CopyButton
|
||||
@clipboardText={{this.pollUrl}}
|
||||
@error={{action "linkAction" "selected"}}
|
||||
@success={{action "linkAction" "copied"}}
|
||||
class="btn btn-secondary cr-poll-link__copy-btn btn-sm"
|
||||
@error={{action 'linkAction' 'selected'}}
|
||||
@success={{action 'linkAction' 'copied'}}
|
||||
class='btn btn-secondary cr-poll-link__copy-btn btn-sm'
|
||||
>
|
||||
{{t "poll.link.copy-label"}}
|
||||
{{t 'poll.link.copy-label'}}
|
||||
<span
|
||||
class="oi oi-clipboard"
|
||||
title={{t "poll.link.copy-label"}}
|
||||
aria-hidden="true"
|
||||
class='oi oi-clipboard'
|
||||
title={{t 'poll.link.copy-label'}}
|
||||
aria-hidden='true'
|
||||
></span>
|
||||
</CopyButton>
|
||||
</p>
|
||||
<small class="text-muted">
|
||||
{{t "poll.share.notice"}}
|
||||
<small class='text-muted'>
|
||||
{{t 'poll.share.notice'}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if this.showExpirationWarning}}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<BsAlert @type="warning" class="expiration-warning">
|
||||
<div class='row'>
|
||||
<div class='col-xs-12'>
|
||||
<BsAlert @type='warning' class='expiration-warning'>
|
||||
{{t
|
||||
"poll.expiration-date-warning"
|
||||
'poll.expiration-date-warning'
|
||||
timeToNow=(format-date-relative poll.expirationDate)
|
||||
}}
|
||||
</BsAlert>
|
||||
|
@ -89,34 +89,39 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="box">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<div class='box'>
|
||||
<ul class='nav nav-tabs' role='tablist'>
|
||||
{{! template-lint-disable no-unknown-arguments-for-builtin-components }}
|
||||
{{!
|
||||
TODO: Refactor to current Bootstrap markup, which uses a regular
|
||||
`<a></a>` element within a `<li class="nav-item"></li>`.
|
||||
}}
|
||||
<LinkTo
|
||||
@route="poll.participation"
|
||||
@route='poll.participation'
|
||||
@model={{poll}}
|
||||
@tagName="li"
|
||||
@activeClass="active"
|
||||
class="participation nav-item"
|
||||
@tagName='li'
|
||||
@activeClass='active'
|
||||
class='participation nav-item'
|
||||
>
|
||||
<LinkTo @route="poll.participation" @model={{poll}} class="nav-link">
|
||||
{{t "poll.tab-title.participation"}}
|
||||
<LinkTo @route='poll.participation' @model={{poll}} class='nav-link'>
|
||||
{{t 'poll.tab-title.participation'}}
|
||||
</LinkTo>
|
||||
</LinkTo>
|
||||
<LinkTo
|
||||
@route="poll.evaluation"
|
||||
@route='poll.evaluation'
|
||||
@model={{poll}}
|
||||
@tagName="li"
|
||||
@activeClass="active"
|
||||
class="evaluation nav-item"
|
||||
@tagName='li'
|
||||
@activeClass='active'
|
||||
class='evaluation nav-item'
|
||||
>
|
||||
<LinkTo @route="poll.evaluation" @model={{poll}} class="nav-link">
|
||||
{{t "poll.tab-title.evaluation"}}
|
||||
<LinkTo @route='poll.evaluation' @model={{poll}} class='nav-link'>
|
||||
{{t 'poll.tab-title.evaluation'}}
|
||||
</LinkTo>
|
||||
</LinkTo>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active">
|
||||
<div class='tab-content'>
|
||||
<div role='tabpanel' class='tab-pane active'>
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -125,32 +130,32 @@
|
|||
{{/let}}
|
||||
|
||||
<BsModal
|
||||
@title={{t "poll.modal.timezoneDiffers.title"}}
|
||||
@title={{t 'poll.modal.timezoneDiffers.title'}}
|
||||
@open={{this.mustChooseTimezone}}
|
||||
@footer={{false}}
|
||||
@closeButton={{false}}
|
||||
@keyboard={{false}}
|
||||
@autoClose={{false}}
|
||||
data-test-modal="choose-timezone"
|
||||
data-test-modal='choose-timezone'
|
||||
as |modal|
|
||||
>
|
||||
<modal.body>
|
||||
<p>
|
||||
{{t "poll.modal.timezoneDiffers.body"}}
|
||||
{{t 'poll.modal.timezoneDiffers.body'}}
|
||||
</p>
|
||||
</modal.body>
|
||||
<modal.footer>
|
||||
<BsButton
|
||||
@onClick={{this.useLocalTimezone}}
|
||||
data-test-button="use-local-timezone"
|
||||
data-test-button='use-local-timezone'
|
||||
>
|
||||
{{t "poll.modal.timezoneDiffers.button.useLocalTimezone"}}
|
||||
{{t 'poll.modal.timezoneDiffers.button.useLocalTimezone'}}
|
||||
</BsButton>
|
||||
<BsButton
|
||||
@onClick={{action (mut this.timezoneChoosen) true}}
|
||||
data-test-button="use-poll-timezone"
|
||||
data-test-button='use-poll-timezone'
|
||||
>
|
||||
{{t "poll.modal.timezoneDiffers.button.usePollTimezone"}}
|
||||
</BsButton>>
|
||||
{{t 'poll.modal.timezoneDiffers.button.usePollTimezone'}}
|
||||
</BsButton>
|
||||
</modal.footer>
|
||||
</BsModal>
|
|
@ -1,19 +1,19 @@
|
|||
import { assert } from '@ember/debug';
|
||||
|
||||
export default function(answerType) {
|
||||
export default function (answerType) {
|
||||
switch (answerType) {
|
||||
case 'YesNo':
|
||||
return [
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up'
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down'
|
||||
}
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
},
|
||||
];
|
||||
|
||||
case 'YesNoMaybe':
|
||||
|
@ -21,18 +21,18 @@ export default function(answerType) {
|
|||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up'
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
},
|
||||
{
|
||||
type: 'maybe',
|
||||
labelTranslation: 'answerTypes.maybe.label',
|
||||
icon: 'glyphicon glyphicon-hand-right'
|
||||
icon: 'glyphicon glyphicon-hand-right',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down'
|
||||
}
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
},
|
||||
];
|
||||
|
||||
case 'FreeText':
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
* implementation by Aaron Toponce:
|
||||
* https://pthree.org/2014/06/25/cryptographically-secure-passphrases-in-d-note/
|
||||
*/
|
||||
export default function(length) {
|
||||
export default function (length) {
|
||||
let passphrase = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const possible =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const randomArray = new Uint32Array(length);
|
||||
|
||||
// Make some attempt at preferring a strong CSPRNG first
|
||||
|
@ -22,12 +23,12 @@ export default function(length) {
|
|||
window.msCrypto.getRandomValues(randomArray);
|
||||
} else {
|
||||
// Android browser, IE Mobile, Opera Mobile, older desktop browsers
|
||||
for (let i = length; i--;) {
|
||||
for (let i = length; i--; ) {
|
||||
randomArray[i] = Math.floor(Math.random() * Math.pow(2, 32));
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = length; j--;) {
|
||||
for (let j = length; j--; ) {
|
||||
passphrase += possible.charAt(Math.floor(randomArray[j] % possible.length));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ module.exports = {
|
|||
javascript: {
|
||||
pattern: 'assets/*.js',
|
||||
limit: '430KB',
|
||||
compression: 'gzip'
|
||||
compression: 'gzip',
|
||||
},
|
||||
css: {
|
||||
pattern: 'assets/*.css',
|
||||
limit: '16KB',
|
||||
compression: 'gzip'
|
||||
}
|
||||
}
|
||||
compression: 'gzip',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = function () {
|
|||
'script-src': ["'self'"],
|
||||
'font-src': ["'self'"],
|
||||
'connect-src': ["'self'"],
|
||||
'img-src': ["'self'", "data:"],
|
||||
'img-src': ["'self'", 'data:'],
|
||||
'style-src': ["'self'"],
|
||||
'media-src': ["'none'"],
|
||||
},
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
self.deprecationWorkflow = self.deprecationWorkflow || {};
|
||||
self.deprecationWorkflow.config = {
|
||||
workflow: [
|
||||
{ handler: "silence", matchId: "ember-cli-page-object.old-collection-api" },
|
||||
{ handler: "silence", matchId: "deprecate-fetch-ember-data-support" },
|
||||
{ handler: "silence", matchId: "ember-runtime.deprecate-copy-copyable" }
|
||||
]
|
||||
{ handler: 'silence', matchId: 'ember-cli-page-object.old-collection-api' },
|
||||
{ handler: 'silence', matchId: 'deprecate-fetch-ember-data-support' },
|
||||
{ handler: 'silence', matchId: 'ember-runtime.deprecate-copy-copyable' },
|
||||
],
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "ember-cli",
|
||||
"version": "3.20.2",
|
||||
"version": "3.28.6",
|
||||
"blueprints": [
|
||||
{
|
||||
"name": "app",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*jshint node:true*/
|
||||
|
||||
module.exports = function(/* environment */) {
|
||||
module.exports = function (/* environment */) {
|
||||
return {
|
||||
/**
|
||||
* The locales that the application needs to support.
|
||||
|
@ -27,7 +27,7 @@ module.exports = function(/* environment */) {
|
|||
* @type {String?}
|
||||
* @default "null"
|
||||
*/
|
||||
fallbackLocale: "en",
|
||||
fallbackLocale: 'en',
|
||||
|
||||
/**
|
||||
* Path where translations are kept. This is relative to the project root.
|
||||
|
@ -120,6 +120,6 @@ module.exports = function(/* environment */) {
|
|||
*/
|
||||
requiresTranslation(/* key, locale */) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function(environment) {
|
||||
module.exports = function (environment) {
|
||||
let ENV = {
|
||||
modulePrefix: 'croodle',
|
||||
environment,
|
||||
|
@ -15,8 +15,8 @@ module.exports = function(environment) {
|
|||
Array: true,
|
||||
Date: false,
|
||||
String: false,
|
||||
Function: true
|
||||
}
|
||||
Function: true,
|
||||
},
|
||||
},
|
||||
|
||||
APP: {
|
||||
|
|
|
@ -2,15 +2,11 @@
|
|||
|
||||
const browsers = [
|
||||
'last 2 Chrome versions',
|
||||
// Last Edge versions are based on Chrome but not shipped to a significant
|
||||
// number of users. Change to `last 2 Edge versions` as soon as Chrome-based
|
||||
// Edge is shipped to all users.
|
||||
'Edge >= 18',
|
||||
'last 2 Firefox versions',
|
||||
'Firefox ESR',
|
||||
'last 2 Safari versions',
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
browsers
|
||||
browsers,
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
module.exports = function(defaults) {
|
||||
module.exports = function (defaults) {
|
||||
let app = new EmberApp(defaults, {
|
||||
autoImport: {
|
||||
forbidEval: true,
|
||||
|
@ -11,21 +11,27 @@ module.exports = function(defaults) {
|
|||
// sjcl requires node's cryto library, which isn't needed
|
||||
// in Browser but causes webpack to bundle a portable version
|
||||
// which increases the build size by an inacceptable amount
|
||||
crypto: "null",
|
||||
crypto: 'null',
|
||||
},
|
||||
},
|
||||
},
|
||||
'buildInfoOptions': {
|
||||
'metaTemplate': 'version={SEMVER}'
|
||||
buildInfoOptions: {
|
||||
metaTemplate: 'version={SEMVER}',
|
||||
},
|
||||
'ember-bootstrap': {
|
||||
importBootstrapCSS: false,
|
||||
'bootstrapVersion': 4,
|
||||
'importBootstrapFont': false,
|
||||
whitelist: ['bs-alert', 'bs-button', 'bs-button-group', 'bs-form', 'bs-modal'],
|
||||
bootstrapVersion: 4,
|
||||
importBootstrapFont: false,
|
||||
whitelist: [
|
||||
'bs-alert',
|
||||
'bs-button',
|
||||
'bs-button-group',
|
||||
'bs-form',
|
||||
'bs-modal',
|
||||
],
|
||||
},
|
||||
'ember-cli-babel': {
|
||||
includePolyfill: true
|
||||
includePolyfill: true,
|
||||
},
|
||||
'ember-composable-helpers': {
|
||||
only: ['array', 'object-at', 'pick'],
|
||||
|
@ -36,7 +42,7 @@ module.exports = function(defaults) {
|
|||
autoprefixer: {
|
||||
browsers: ['last 2 ios version'],
|
||||
cascade: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
},
|
||||
sassOptions: {
|
||||
sourceMapEmbed: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
browser: false
|
||||
}
|
||||
browser: false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -41,15 +41,19 @@ module.exports = {
|
|||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec('composer install --no-dev', {
|
||||
cwd: outputPath
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
exec(
|
||||
'composer install --no-dev',
|
||||
{
|
||||
cwd: outputPath,
|
||||
},
|
||||
(err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -58,5 +62,5 @@ module.exports = {
|
|||
unlink(`${outputPath}/composer.lock`),
|
||||
]);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default function() {
|
||||
this.namespace = '/api/index.php'; // make this `api`, for example, if your API is namespaced
|
||||
this.timing = 400; // delay for each request, automatically set to 0 during testing
|
||||
export default function () {
|
||||
this.namespace = '/api/index.php'; // make this `api`, for example, if your API is namespaced
|
||||
this.timing = 400; // delay for each request, automatically set to 0 during testing
|
||||
|
||||
this.get('/polls/:id');
|
||||
this.post('/polls');
|
||||
|
|
|
@ -9,14 +9,14 @@ export default Factory.extend({
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Ja'
|
||||
label: 'Ja',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'Nein'
|
||||
}
|
||||
label: 'Nein',
|
||||
},
|
||||
],
|
||||
answerType: 'YesNo',
|
||||
creationDate: '2015-04-01T11:11:11.111Z',
|
||||
|
@ -27,11 +27,11 @@ export default Factory.extend({
|
|||
isDateTime: false,
|
||||
options: [
|
||||
{
|
||||
title: '2017-12-24'
|
||||
title: '2017-12-24',
|
||||
},
|
||||
{
|
||||
title: '2018-01-01'
|
||||
}
|
||||
title: '2018-01-01',
|
||||
},
|
||||
],
|
||||
pollType: 'FindADate',
|
||||
title: 'default title',
|
||||
|
@ -50,8 +50,8 @@ export default Factory.extend({
|
|||
'options',
|
||||
'pollType',
|
||||
'timezone',
|
||||
'title'
|
||||
'title',
|
||||
];
|
||||
encrypt(propertiesToEncrypt, poll, server);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,15 +3,11 @@ import { Factory } from 'ember-cli-mirage';
|
|||
import encrypt from '../utils/encrypt';
|
||||
|
||||
export default Factory.extend({
|
||||
creationDate: (new Date()).toISOString(),
|
||||
creationDate: new Date().toISOString(),
|
||||
name: 'John Doe',
|
||||
selections: [],
|
||||
afterCreate(user, server) {
|
||||
let propertiesToEncrypt = [
|
||||
'creationDate',
|
||||
'name',
|
||||
'selections'
|
||||
];
|
||||
let propertiesToEncrypt = ['creationDate', 'name', 'selections'];
|
||||
encrypt(propertiesToEncrypt, user, server);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -51,6 +51,6 @@ export default class {
|
|||
* @public
|
||||
*/
|
||||
reset() {
|
||||
this._ids = {};
|
||||
this._ids = {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Model, hasMany } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
users: hasMany('user')
|
||||
users: hasMany('user'),
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
poll: belongsTo('poll')
|
||||
poll: belongsTo('poll'),
|
||||
});
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
export default function(/* server */) {
|
||||
|
||||
export default function (/* server */) {
|
||||
/*
|
||||
Seed your development database using your factories.
|
||||
This data will not be loaded in your tests.
|
||||
|
||||
Make sure to define a factory for each model you want to create.
|
||||
*/
|
||||
|
||||
// server.createList('post', 10);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ export default RestSerializer.extend({
|
|||
normalize(payload) {
|
||||
let [type] = Object.keys(payload);
|
||||
let attrs = payload[type];
|
||||
let { belongsToAssociations, hasManyAssociations } = this.registry.schema._registry[type].class.prototype;
|
||||
let { belongsToAssociations, hasManyAssociations } =
|
||||
this.registry.schema._registry[type].class.prototype;
|
||||
|
||||
let jsonApiPayload = {
|
||||
data: {
|
||||
type: pluralize(type)
|
||||
}
|
||||
type: pluralize(type),
|
||||
},
|
||||
};
|
||||
|
||||
Object.keys(attrs).forEach((key) => {
|
||||
|
@ -32,8 +33,12 @@ export default RestSerializer.extend({
|
|||
jsonApiPayload.data.relationships = {};
|
||||
}
|
||||
|
||||
let association = belongsToAssociations.hasOwnProperty(key) ? belongsToAssociations[key] : hasManyAssociations[key];
|
||||
let associationType = belongsToAssociations.hasOwnProperty(key) ? 'belongsTo' : 'hasMany';
|
||||
let association = belongsToAssociations.hasOwnProperty(key)
|
||||
? belongsToAssociations[key]
|
||||
: hasManyAssociations[key];
|
||||
let associationType = belongsToAssociations.hasOwnProperty(key)
|
||||
? 'belongsTo'
|
||||
: 'hasMany';
|
||||
let associationModel = association.modelName;
|
||||
let relationshipObject = {};
|
||||
|
||||
|
@ -41,7 +46,7 @@ export default RestSerializer.extend({
|
|||
case 'belongsTo':
|
||||
relationshipObject.data = {
|
||||
type: associationModel,
|
||||
id: attrs[key]
|
||||
id: attrs[key],
|
||||
};
|
||||
break;
|
||||
case 'hasMany':
|
||||
|
@ -49,7 +54,7 @@ export default RestSerializer.extend({
|
|||
attrs[key].forEach((value) => {
|
||||
relationshipObject.data.push({
|
||||
type: associationModel,
|
||||
id: value
|
||||
id: value,
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
@ -68,5 +73,5 @@ export default RestSerializer.extend({
|
|||
|
||||
return jsonApiPayload;
|
||||
},
|
||||
serializeIds: 'always'
|
||||
serializeIds: 'always',
|
||||
});
|
||||
|
|
|
@ -3,5 +3,5 @@ import ApplicationSerializer from './application';
|
|||
|
||||
export default ApplicationSerializer.extend({
|
||||
embed: true,
|
||||
include: ['users']
|
||||
include: ['users'],
|
||||
});
|
||||
|
|
|
@ -9,19 +9,20 @@ import { isArray } from '@ember/array';
|
|||
import { get } from '@ember/object';
|
||||
import sjcl from 'sjcl';
|
||||
|
||||
export default function(propertiesToEncrypt, model) {
|
||||
export default function (propertiesToEncrypt, model) {
|
||||
assert('first argument must be an array', isArray(propertiesToEncrypt));
|
||||
assert('model must have an encryptionKey property which isn\'t empty', isPresent(get(model, 'encryptionKey')));
|
||||
assert(
|
||||
"model must have an encryptionKey property which isn't empty",
|
||||
isPresent(model.encryptionKey)
|
||||
);
|
||||
|
||||
let passphrase = get(model, 'encryptionKey');
|
||||
let passphrase = model.encryptionKey;
|
||||
let data = {
|
||||
encryptionKey: undefined
|
||||
encryptionKey: undefined,
|
||||
};
|
||||
|
||||
propertiesToEncrypt.forEach((propertyToEncrypt) => {
|
||||
let value = JSON.stringify(
|
||||
get(model, propertyToEncrypt)
|
||||
);
|
||||
let value = JSON.stringify(get(model, propertyToEncrypt));
|
||||
data[propertyToEncrypt] = sjcl.encrypt(passphrase, value);
|
||||
});
|
||||
|
||||
|
|
51
package.json
51
package.json
|
@ -11,20 +11,24 @@
|
|||
},
|
||||
"scripts": {
|
||||
"build": "ember build --environment=production",
|
||||
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*",
|
||||
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
|
||||
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
|
||||
"lint:hbs": "ember-template-lint .",
|
||||
"lint:js": "eslint .",
|
||||
"lint:hbs:fix": "ember-template-lint . --fix",
|
||||
"lint:js": "eslint . --cache",
|
||||
"release": "release-it",
|
||||
"lint:js:fix": "eslint . --fix",
|
||||
"start": "ember serve",
|
||||
"test": "npm-run-all lint:* test:*",
|
||||
"test": "npm-run-all lint test:*",
|
||||
"test:ember": "ember test",
|
||||
"test:bundlesize": "ember bundlesize:test",
|
||||
"test:csp-header": "grep \"`ember csp-headers --environment production --silent 2>&1 | sed 's/ $//'`\" public/.htaccess || (echo \"CSP headers in public/.htaccess does not match configuration\" && exit 1)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@glimmer/component": "^1.0.1",
|
||||
"@glimmer/tracking": "^1.0.0",
|
||||
"@ember/test-helpers": "^2.6.0",
|
||||
"@glimmer/component": "^1.0.4",
|
||||
"@glimmer/tracking": "^1.0.4",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"bootstrap": "^4.3.1",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
|
@ -32,10 +36,10 @@
|
|||
"ember-awesome-macros": "^6.0.0",
|
||||
"ember-bootstrap": "^5.0.0",
|
||||
"ember-classic-decorator": "^3.0.0",
|
||||
"ember-cli": "~3.28.0",
|
||||
"ember-cli": "~3.28.6",
|
||||
"ember-cli-acceptance-test-helpers": "^1.0.0",
|
||||
"ember-cli-app-version": "^6.0.0",
|
||||
"ember-cli-babel": "^7.21.0",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-browser-navigation-button-test-helper": "^0.3.0",
|
||||
"ember-cli-browserstack": "^2.0.0",
|
||||
"ember-cli-bundlesize": "^0.3.0",
|
||||
|
@ -44,43 +48,48 @@
|
|||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-deprecation-workflow": "^2.0.0",
|
||||
"ember-cli-flash": "^2.0.0",
|
||||
"ember-cli-htmlbars": "^5.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.0.2",
|
||||
"ember-cli-htmlbars": "^5.7.2",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-mirage": "^2.0.0",
|
||||
"ember-cli-page-object": "^1.11.0",
|
||||
"ember-cli-sass": "^10.0.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-uglify": "^3.0.0",
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-composable-helpers": "^4.0.0",
|
||||
"ember-data": "~3.28.0",
|
||||
"ember-data": "~3.28.6",
|
||||
"ember-data-model-fragments": "^6.0.0",
|
||||
"ember-decorators": "^6.1.1",
|
||||
"ember-export-application-global": "^2.0.1",
|
||||
"ember-fetch": "^8.0.1",
|
||||
"ember-fetch": "^8.1.1",
|
||||
"ember-intl": "^4.2.2",
|
||||
"ember-load-initializers": "^2.1.1",
|
||||
"ember-load-initializers": "^2.1.2",
|
||||
"ember-math-helpers": "^2.8.1",
|
||||
"ember-maybe-import-regenerator": "^0.1.6",
|
||||
"ember-modifier": "^3.2.7",
|
||||
"ember-page-title": "^6.0.0",
|
||||
"ember-page-title": "^6.2.2",
|
||||
"ember-power-calendar": "^0.20.0",
|
||||
"ember-power-calendar-luxon": "^0.5.0",
|
||||
"ember-qunit": "^4.6.0",
|
||||
"ember-resolver": "^8.0.0",
|
||||
"ember-source": "~3.28.0",
|
||||
"ember-template-lint": "^2.9.1",
|
||||
"ember-qunit": "^5.1.5",
|
||||
"ember-resolver": "^8.0.3",
|
||||
"ember-source": "~3.28.8",
|
||||
"ember-template-lint": "^3.15.0",
|
||||
"ember-test-selectors": "^5.0.0",
|
||||
"ember-transition-helper": "^1.0.0",
|
||||
"ember-truth-helpers": "^3.0.0",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-plugin-ember": "^8.9.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.5.8",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-qunit": "^6.2.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"lerna-changelog": "^1.0.0",
|
||||
"loader.js": "^4.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"open-iconic": "^1.1.1",
|
||||
"qunit-dom": "^1.2.0",
|
||||
"prettier": "^2.5.1",
|
||||
"qunit": "^2.17.2",
|
||||
"qunit-dom": "^1.6.0",
|
||||
"release-it": "^16.0.0",
|
||||
"release-it-lerna-changelog": "^5.0.0",
|
||||
"sass": "^1.19.0",
|
||||
|
|
|
@ -15,9 +15,7 @@ header.style.color = '#B33A3A';
|
|||
var icon = document.createElement('span');
|
||||
icon.className = 'oi oi-warning';
|
||||
header.appendChild(icon);
|
||||
header.appendChild(
|
||||
document.createTextNode('Your Browser is not supported!')
|
||||
);
|
||||
header.appendChild(document.createTextNode('Your Browser is not supported!'));
|
||||
header.appendChild(icon.cloneNode());
|
||||
container.appendChild(header);
|
||||
|
||||
|
|
|
@ -6,14 +6,9 @@ module.exports = {
|
|||
timeout: 1200,
|
||||
browser_start_timeout: 2000,
|
||||
browser_disconnect_timeout: 300,
|
||||
launch_in_ci: [
|
||||
'BS_MS_Edge',
|
||||
'BS_Safari_Current',
|
||||
],
|
||||
launch_in_ci: ['BS_MS_Edge', 'BS_Safari_Current'],
|
||||
|
||||
'launch_in_dev': [
|
||||
'Chrome'
|
||||
],
|
||||
launch_in_dev: ['Chrome'],
|
||||
|
||||
launchers: {
|
||||
BS_Chrome_Current: {
|
||||
|
@ -106,5 +101,5 @@ module.exports = {
|
|||
],
|
||||
protocol: 'browser',
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
38
testem.js
38
testem.js
|
@ -3,13 +3,8 @@
|
|||
module.exports = {
|
||||
test_page: 'tests/index.html?hidepassed',
|
||||
disable_watching: true,
|
||||
launch_in_ci: [
|
||||
'Chrome',
|
||||
'Firefox',
|
||||
],
|
||||
launch_in_dev: [
|
||||
'Chrome'
|
||||
],
|
||||
launch_in_ci: ['Chrome'],
|
||||
launch_in_dev: ['Chrome'],
|
||||
browser_start_timeout: 120,
|
||||
browser_args: {
|
||||
Chrome: {
|
||||
|
@ -21,23 +16,18 @@ module.exports = {
|
|||
'--disable-software-rasterizer',
|
||||
'--mute-audio',
|
||||
'--remote-debugging-port=0',
|
||||
'--window-size=1440,900'
|
||||
].filter(Boolean)
|
||||
'--window-size=1440,900',
|
||||
].filter(Boolean),
|
||||
},
|
||||
Firefox: {
|
||||
ci: [
|
||||
'--headless',
|
||||
'--window-size=1440,900',
|
||||
]
|
||||
ci: ['--headless', '--window-size=1440,900'],
|
||||
},
|
||||
},
|
||||
proxies: {
|
||||
'/': {
|
||||
target: 'http://localhost:4200',
|
||||
onlyContentTypes: [
|
||||
'json'
|
||||
]
|
||||
}
|
||||
onlyContentTypes: ['json'],
|
||||
},
|
||||
},
|
||||
launchers: {
|
||||
SL_chrome: {
|
||||
|
@ -54,7 +44,7 @@ module.exports = {
|
|||
'--no-ct',
|
||||
'--u',
|
||||
],
|
||||
protocol: 'browser'
|
||||
protocol: 'browser',
|
||||
},
|
||||
SL_firefox: {
|
||||
exe: 'ember',
|
||||
|
@ -70,7 +60,7 @@ module.exports = {
|
|||
'--no-ct',
|
||||
'--u',
|
||||
],
|
||||
protocol: 'browser'
|
||||
protocol: 'browser',
|
||||
},
|
||||
SL_edge: {
|
||||
exe: 'ember',
|
||||
|
@ -84,7 +74,7 @@ module.exports = {
|
|||
'--no-ct',
|
||||
'--u',
|
||||
],
|
||||
protocol: 'browser'
|
||||
protocol: 'browser',
|
||||
},
|
||||
SL_ie: {
|
||||
exe: 'ember',
|
||||
|
@ -102,7 +92,7 @@ module.exports = {
|
|||
'--no-ct',
|
||||
'--u',
|
||||
],
|
||||
protocol: 'browser'
|
||||
protocol: 'browser',
|
||||
},
|
||||
SL_safari: {
|
||||
exe: 'ember',
|
||||
|
@ -118,7 +108,7 @@ module.exports = {
|
|||
'--no-ct',
|
||||
'--u',
|
||||
],
|
||||
protocol: 'browser'
|
||||
}
|
||||
}
|
||||
protocol: 'browser',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,15 +2,17 @@ import { visit } from '@ember/test-helpers';
|
|||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
|
||||
module('Acceptance | build', function(hooks) {
|
||||
module('Acceptance | build', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
test('version is included as html meta tag', async function(assert) {
|
||||
test('version is included as html meta tag', async function (assert) {
|
||||
await visit('/');
|
||||
|
||||
// head is not available through `find()`, `assert.dom()` or `this.element.querySelector()`
|
||||
// cause they are scoped to `#ember-testing-container`.
|
||||
let buildInfoEl = document.head.querySelector('head meta[name="build-info"]');
|
||||
let buildInfoEl = document.head.querySelector(
|
||||
'head meta[name="build-info"]'
|
||||
);
|
||||
assert.ok(buildInfoEl, 'tag exists');
|
||||
|
||||
let content = buildInfoEl.content;
|
||||
|
@ -20,18 +22,23 @@ module('Acceptance | build', function(hooks) {
|
|||
);
|
||||
});
|
||||
|
||||
test('CSP meta tag is present and before any dangerous element', async function(assert) {
|
||||
test('CSP meta tag is present and before any dangerous element', async function (assert) {
|
||||
await visit('/');
|
||||
|
||||
// `find()`, `assert.dom()` and `this.element.querySelector()` are all scoped to `#testing-container`
|
||||
// and therefore don't have access to head
|
||||
assert.ok(document.head.querySelector('meta[http-equiv="Content-Security-Policy"]'), 'CSP meta tag exists');
|
||||
assert.ok(
|
||||
document.head.querySelector('meta[http-equiv="Content-Security-Policy"]'),
|
||||
'CSP meta tag exists'
|
||||
);
|
||||
|
||||
// this only covers dynamically created elements not the ones defined in `app/index.html` cause
|
||||
// that one is replaced by `tests/index.html` for testing.
|
||||
['link', 'script', 'style'].forEach((type) => {
|
||||
assert.notOk(
|
||||
document.head.querySelector(`${type} meta[http-equiv="Content-Security-Policy"]`),
|
||||
document.head.querySelector(
|
||||
`${type} meta[http-equiv="Content-Security-Policy"]`
|
||||
),
|
||||
'CSP meta tag does not have a silbing of type ${type}'
|
||||
);
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,21 +2,26 @@ import { fillIn, find, visit } from '@ember/test-helpers';
|
|||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
|
||||
module('Acceptance | i18n', function(hooks) {
|
||||
hooks.beforeEach(function() {
|
||||
module('Acceptance | i18n', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'de');
|
||||
});
|
||||
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
test('locale is saved in localStorage', async function(assert) {
|
||||
test('locale is saved in localStorage', async function (assert) {
|
||||
await visit('/');
|
||||
assert.equal(find('.language-select').value, 'de', 'picks up locale in locale storage');
|
||||
assert.equal(
|
||||
find('.language-select').value,
|
||||
'de',
|
||||
'picks up locale in locale storage'
|
||||
);
|
||||
|
||||
await fillIn('.language-select', 'en');
|
||||
assert.equal(find('.language-select').value, 'en');
|
||||
assert.equal(
|
||||
window.localStorage.getItem('locale'), 'en',
|
||||
window.localStorage.getItem('locale'),
|
||||
'en',
|
||||
'persisted in localeStorage'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -8,12 +8,12 @@ import pollParticipate from 'croodle/tests/helpers/poll-participate';
|
|||
import PollParticipationPage from 'croodle/tests/pages/poll/participation';
|
||||
import PollEvaluationPage from 'croodle/tests/pages/poll/evaluation';
|
||||
|
||||
module('Acceptance | legacy support', function(hooks) {
|
||||
module('Acceptance | legacy support', function (hooks) {
|
||||
let yesLabel;
|
||||
let maybeLabel;
|
||||
let noLabel;
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'en');
|
||||
});
|
||||
|
||||
|
@ -21,58 +21,115 @@ module('Acceptance | legacy support', function(hooks) {
|
|||
setupIntl(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
yesLabel = t('answerTypes.yes.label').toString();
|
||||
maybeLabel = t('answerTypes.maybe.label').toString();
|
||||
noLabel = t('answerTypes.no.label').toString();
|
||||
});
|
||||
|
||||
|
||||
test('show a default poll created with v0.3.0', async function(assert) {
|
||||
test('show a default poll created with v0.3.0', async function (assert) {
|
||||
const encryptionKey = '5MKFuNTKILUXw6RuqkAw6ooZw4k3mWWx98ZQw8vH';
|
||||
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
// property 'id' of answers has been renamed to 'type' in v0.4.0
|
||||
answers: [{ 'id': 'yes','labelTranslation': 'answerTypes.yes.label','icon': 'glyphicon glyphicon-thumbs-up','label': 'Ja' },{ 'id': 'maybe','labelTranslation': 'answerTypes.maybe.label','icon': 'glyphicon glyphicon-hand-right','label': 'Vielleicht' },{ 'id': 'no','labelTranslation': 'answerTypes.no.label','icon': 'glyphicon glyphicon-thumbs-down','label': 'Nein' }],
|
||||
options: [{ 'title': '2015-12-24T17:00:00.000Z' },{ 'title': '2015-12-24T19:00:00.000Z' },{ 'title': '2015-12-31T22:59:00.000Z' }],
|
||||
answers: [
|
||||
{
|
||||
id: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Ja',
|
||||
},
|
||||
{
|
||||
id: 'maybe',
|
||||
labelTranslation: 'answerTypes.maybe.label',
|
||||
icon: 'glyphicon glyphicon-hand-right',
|
||||
label: 'Vielleicht',
|
||||
},
|
||||
{
|
||||
id: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'Nein',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{ title: '2015-12-24T17:00:00.000Z' },
|
||||
{ title: '2015-12-24T19:00:00.000Z' },
|
||||
{ title: '2015-12-31T22:59:00.000Z' },
|
||||
],
|
||||
users: [
|
||||
this.server.create('user', {
|
||||
encryptionKey,
|
||||
name: 'Fritz Bauer',
|
||||
// selections.value was renamed to selections.label
|
||||
// selections.id was renamed to selections.type
|
||||
selections: [{ 'value': { 'id': 'yes','labelTranslation': 'answerTypes.yes.label','icon': 'glyphicon glyphicon-thumbs-up','label': 'Ja' } },{ 'value': { 'id': 'no','labelTranslation': 'answerTypes.no.label','icon': 'glyphicon glyphicon-thumbs-down','label': 'Nein' } },{ 'value': { 'id': 'no','labelTranslation': 'answerTypes.no.label','icon': 'glyphicon glyphicon-thumbs-down','label': 'Nein' } }],
|
||||
selections: [
|
||||
{
|
||||
value: {
|
||||
id: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Ja',
|
||||
},
|
||||
},
|
||||
{
|
||||
value: {
|
||||
id: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'Nein',
|
||||
},
|
||||
},
|
||||
{
|
||||
value: {
|
||||
id: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'Nein',
|
||||
},
|
||||
},
|
||||
],
|
||||
// version tag had have wrong format
|
||||
version: 'v0.3-0'
|
||||
})
|
||||
version: 'v0.3-0',
|
||||
}),
|
||||
],
|
||||
// version tag had have wrong format
|
||||
version: 'v0.3-0'
|
||||
version: 'v0.3-0',
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
assert.deepEqual(
|
||||
PollParticipationPage.options().labels,
|
||||
[
|
||||
Intl.DateTimeFormat('en-US', { dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-24T17:00:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', { timeStyle: "short" }).format(new Date('2015-12-24T19:00:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', { dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-31T22:59:00.000Z')),
|
||||
]
|
||||
);
|
||||
assert.deepEqual(
|
||||
PollParticipationPage.options().answers,
|
||||
[yesLabel, maybeLabel, noLabel]
|
||||
);
|
||||
assert.deepEqual(PollParticipationPage.options().labels, [
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-24T17:00:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', { timeStyle: 'short' }).format(
|
||||
new Date('2015-12-24T19:00:00.000Z')
|
||||
),
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-31T22:59:00.000Z')),
|
||||
]);
|
||||
assert.deepEqual(PollParticipationPage.options().answers, [
|
||||
yesLabel,
|
||||
maybeLabel,
|
||||
noLabel,
|
||||
]);
|
||||
|
||||
await switchTab('evaluation');
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'Fritz Bauer')[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Fritz Bauer'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, noLabel, noLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, noLabel, noLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
|
||||
|
@ -82,21 +139,29 @@ module('Acceptance | legacy support', function(hooks) {
|
|||
await pollParticipate('Hermann Langbein', ['yes', 'maybe', 'yes']);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
|
||||
participant = PollEvaluationPage.participants.filterBy('name', 'Hermann Langbein')[0];
|
||||
participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Hermann Langbein'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, maybeLabel, yesLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, maybeLabel, yesLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('show a poll using free text created with v0.3.0', async function(assert) {
|
||||
test('show a poll using free text created with v0.3.0', async function (assert) {
|
||||
let encryptionKey = 'Rre6dAGOYLW9gYKOP4LhX7Qwfhe5Th3je0uKDtyy';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
answerType: 'FreeText',
|
||||
answers: [],
|
||||
options: [{ 'title': 'apple pie' }, { 'title': 'pecan pie' }, { 'title': 'plum pie' }],
|
||||
options: [
|
||||
{ title: 'apple pie' },
|
||||
{ title: 'pecan pie' },
|
||||
{ title: 'plum pie' },
|
||||
],
|
||||
pollType: 'MakeAPoll',
|
||||
users: [
|
||||
this.server.create('user', {
|
||||
|
@ -104,46 +169,59 @@ module('Acceptance | legacy support', function(hooks) {
|
|||
name: 'Paul Levi',
|
||||
// selections.value was renamed to selections.label
|
||||
// selections.id was renamed to selections.type
|
||||
selections: [{ 'value': 'would be great!' }, { 'value': 'no way' }, { 'value': 'if I had to' }],
|
||||
selections: [
|
||||
{ value: 'would be great!' },
|
||||
{ value: 'no way' },
|
||||
{ value: 'if I had to' },
|
||||
],
|
||||
// version tag had have wrong format
|
||||
version: 'v0.3-0'
|
||||
})
|
||||
version: 'v0.3-0',
|
||||
}),
|
||||
],
|
||||
// version tag had have wrong format
|
||||
version: 'v0.3-0'
|
||||
version: 'v0.3-0',
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
assert.deepEqual(
|
||||
PollParticipationPage.options().labels,
|
||||
[
|
||||
'apple pie',
|
||||
'pecan pie',
|
||||
'plum pie'
|
||||
]
|
||||
);
|
||||
assert.deepEqual(PollParticipationPage.options().labels, [
|
||||
'apple pie',
|
||||
'pecan pie',
|
||||
'plum pie',
|
||||
]);
|
||||
|
||||
await switchTab('evaluation');
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'Paul Levi')[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Paul Levi'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), ['would be great!', 'no way', 'if I had to'],
|
||||
participant.selections.map((_) => _.answer),
|
||||
['would be great!', 'no way', 'if I had to'],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
|
||||
await switchTab('participation');
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
|
||||
await pollParticipate('Hermann Langbein', ["I don't care", 'would be awesome', "can't imagine anything better"]);
|
||||
await pollParticipate('Hermann Langbein', [
|
||||
"I don't care",
|
||||
'would be awesome',
|
||||
"can't imagine anything better",
|
||||
]);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
|
||||
participant = PollEvaluationPage.participants.filterBy('name', 'Hermann Langbein')[0];
|
||||
participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Hermann Langbein'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), ['I don\'t care', 'would be awesome', 'can\'t imagine anything better'],
|
||||
participant.selections.map((_) => _.answer),
|
||||
["I don't care", 'would be awesome', "can't imagine anything better"],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
currentURL,
|
||||
currentRouteName,
|
||||
waitFor,
|
||||
visit
|
||||
visit,
|
||||
} from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
|
@ -14,11 +14,11 @@ import { setupMirage } from 'ember-cli-mirage/test-support';
|
|||
import PollEvaluationPage from 'croodle/tests/pages/poll/evaluation';
|
||||
import pollParticipate from 'croodle/tests/helpers/poll-participate';
|
||||
|
||||
module('Acceptance | participate in a poll', function(hooks) {
|
||||
module('Acceptance | participate in a poll', function (hooks) {
|
||||
let yesLabel;
|
||||
let noLabel;
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'en');
|
||||
});
|
||||
|
||||
|
@ -26,19 +26,23 @@ module('Acceptance | participate in a poll', function(hooks) {
|
|||
setupIntl(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
yesLabel = t('answerTypes.yes.label').toString();
|
||||
noLabel = t('answerTypes.no.label').toString();
|
||||
});
|
||||
|
||||
test('participate in a default poll', async function(assert) {
|
||||
test('participate in a default poll', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.participation', 'poll is redirected to poll.participation');
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll.participation',
|
||||
'poll is redirected to poll.participation'
|
||||
);
|
||||
|
||||
await pollParticipate('Max Meiner', ['yes', 'no']);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
|
@ -47,142 +51,202 @@ module('Acceptance | participate in a poll', function(hooks) {
|
|||
`encryptionKey=${encryptionKey}`,
|
||||
'encryption key is part of query params'
|
||||
);
|
||||
assert.equal(PollEvaluationPage.participants.length, 1, 'user is added to participants table');
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'Max Meiner')[0];
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
1,
|
||||
'user is added to participants table'
|
||||
);
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Max Meiner'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, noLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, noLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
|
||||
await click('.nav .participation');
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
assert.equal(find('.name input').value, '', 'input for name is cleared');
|
||||
assert.ok(
|
||||
!findAll('input[type="radio"]').toArray().some((el) => el.checked),
|
||||
assert.notOk(
|
||||
findAll('input[type="radio"]')
|
||||
.toArray()
|
||||
.some((el) => el.checked),
|
||||
'radios are cleared'
|
||||
);
|
||||
|
||||
await pollParticipate('Peter Müller', ['yes', 'yes']);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(PollEvaluationPage.participants.length, 2, 'user is added to participants table');
|
||||
participant = PollEvaluationPage.participants.filterBy('name', 'Peter Müller')[0];
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
2,
|
||||
'user is added to participants table'
|
||||
);
|
||||
participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Peter Müller'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, yesLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, yesLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('participate in a poll using freetext', async function(assert) {
|
||||
test('participate in a poll using freetext', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
answerType: 'FreeText',
|
||||
answers: [],
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`)
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
|
||||
await pollParticipate('Max Manus', ['answer 1', 'answer 2']);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(PollEvaluationPage.participants.length, 1, 'user is added to participants table');
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
1,
|
||||
'user is added to participants table'
|
||||
);
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'Max Manus')[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Max Manus'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), ['answer 1', 'answer 2'],
|
||||
participant.selections.map((_) => _.answer),
|
||||
['answer 1', 'answer 2'],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('participate in a poll which does not force an answer to all options', async function(assert) {
|
||||
test('participate in a poll which does not force an answer to all options', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
forceAnswer: false
|
||||
forceAnswer: false,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
|
||||
await pollParticipate('Karl Käfer', ['yes', null]);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(PollEvaluationPage.participants.length, 1, 'user is added to participants table');
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
1,
|
||||
'user is added to participants table'
|
||||
);
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'Karl Käfer')[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'Karl Käfer'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, ''],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, ''],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('participate in a poll which allows anonymous participation', async function(assert) {
|
||||
test('participate in a poll which allows anonymous participation', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
anonymousUser: true,
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
|
||||
await pollParticipate(null, ['yes', 'no']);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(PollEvaluationPage.participants.length, 1, 'user is added to participants table');
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
1,
|
||||
'user is added to participants table'
|
||||
);
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', '')[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, noLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, noLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('network connectivity errors', async function(assert) {
|
||||
test('network connectivity errors', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
this.server.post('/users', undefined, 503);
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
assert.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
.isNotVisible('failed saving notification is not shown before attempt to save');
|
||||
assert
|
||||
.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
.isNotVisible(
|
||||
'failed saving notification is not shown before attempt to save'
|
||||
);
|
||||
|
||||
await pollParticipate('John Doe', ['yes', 'no']);
|
||||
assert.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
assert
|
||||
.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
.isVisible('user gets notified that saving failed');
|
||||
|
||||
this.server.post('/users');
|
||||
|
||||
await click('[data-test-modal="saving-failed"] [data-test-button="retry"]');
|
||||
assert.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
.isNotVisible('Notification is hidden after another save attempt was successful');
|
||||
assert
|
||||
.dom('[data-test-modal="saving-failed"] .modal-content')
|
||||
.isNotVisible(
|
||||
'Notification is hidden after another save attempt was successful'
|
||||
);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(PollEvaluationPage.participants.length, 1, 'user is added to participants table');
|
||||
assert.equal(
|
||||
PollEvaluationPage.participants.length,
|
||||
1,
|
||||
'user is added to participants table'
|
||||
);
|
||||
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', 'John Doe')[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
'John Doe'
|
||||
)[0];
|
||||
assert.ok(participant, 'user exists in participants table');
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer), [yesLabel, noLabel],
|
||||
participant.selections.map((_) => _.answer),
|
||||
[yesLabel, noLabel],
|
||||
'participants table shows correct answers for new participant'
|
||||
);
|
||||
});
|
||||
|
||||
test('shows loading spinner while submitting', async function(assert) {
|
||||
test('shows loading spinner while submitting', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
let resolveSubmission;
|
||||
let resolveSubmissionWith;
|
||||
this.server.post('/users', function(schema) {
|
||||
this.server.post('/users', function (schema) {
|
||||
return new Promise((resolve) => {
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
|
||||
|
@ -191,90 +255,143 @@ module('Acceptance | participate in a poll', function(hooks) {
|
|||
});
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
pollParticipate('John Doe', ['yes', 'no']);
|
||||
|
||||
await waitFor('[data-test-button="submit"] .spinner-border', {
|
||||
timeoutMessage: 'timeout while waiting for loading spinner to appear',
|
||||
});
|
||||
assert.ok(true, 'loading spinner shown cause otherwise there would have been a timeout');
|
||||
assert.ok(
|
||||
true,
|
||||
'loading spinner shown cause otherwise there would have been a timeout'
|
||||
);
|
||||
|
||||
// resolve promise for test to finish
|
||||
// need to resolve with a valid response cause otherwise Ember Data would throw
|
||||
resolveSubmission(resolveSubmissionWith);
|
||||
});
|
||||
|
||||
module('validation', function() {
|
||||
test('shows validation errors for participation form on submit', async function(assert) {
|
||||
module('validation', function () {
|
||||
test('shows validation errors for participation form on submit', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
await click('button[type="submit"]');
|
||||
|
||||
assert.dom('[data-test-form-element="name"] input').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]').hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="name"] input')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]')
|
||||
.hasClass('is-invalid');
|
||||
|
||||
assert.dom('[data-test-form-element="name"] input').isFocused();
|
||||
|
||||
assert.equal(currentRouteName(), 'poll.participation', 'invalid form prevents a transition');
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll.participation',
|
||||
'invalid form prevents a transition'
|
||||
);
|
||||
});
|
||||
|
||||
test('does not show validation error for name if poll allows anonymous participation', async function(assert) {
|
||||
test('does not show validation error for name if poll allows anonymous participation', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
anonymousUser: true,
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
await click('button[type="submit"]');
|
||||
|
||||
assert.dom('[data-test-form-element="name"] input').hasClass('is-valid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]').hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]')
|
||||
.hasClass('is-invalid');
|
||||
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]').isFocused();
|
||||
assert.equal(currentRouteName(), 'poll.participation', 'invalid form prevents a transition');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]')
|
||||
.isFocused();
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll.participation',
|
||||
'invalid form prevents a transition'
|
||||
);
|
||||
});
|
||||
|
||||
test('does not show validation error for option inputs if poll does not force an answer to each option', async function(assert) {
|
||||
test('does not show validation error for option inputs if poll does not force an answer to each option', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
forceAnswer: false
|
||||
forceAnswer: false,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
await click('button[type="submit"]');
|
||||
|
||||
assert.dom('[data-test-form-element="name"] input').hasClass('is-invalid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]').hasClass('is-valid');
|
||||
assert.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]').hasClass('is-valid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]').hasClass('is-valid');
|
||||
assert.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]').hasClass('is-valid');
|
||||
assert
|
||||
.dom('[data-test-form-element="name"] input')
|
||||
.hasClass('is-invalid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="yes"]')
|
||||
.hasClass('is-valid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2017-12-24"] input[id$="no"]')
|
||||
.hasClass('is-valid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="yes"]')
|
||||
.hasClass('is-valid');
|
||||
assert
|
||||
.dom('[data-test-form-element="option-2018-01-01"] input[id$="no"]')
|
||||
.hasClass('is-valid');
|
||||
|
||||
assert.dom('[data-test-form-element="name"] input').isFocused();
|
||||
|
||||
assert.equal(currentRouteName(), 'poll.participation', 'invalid form prevents a transition');
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll.participation',
|
||||
'invalid form prevents a transition'
|
||||
);
|
||||
});
|
||||
|
||||
test('does not show validation errors while saving participation', async function(assert) {
|
||||
test('does not show validation errors while saving participation', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
let resolveSubmission;
|
||||
let resolveSubmissionWith;
|
||||
this.server.post('/users', function(schema) {
|
||||
this.server.post('/users', function (schema) {
|
||||
return new Promise((resolve) => {
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
|
||||
|
@ -283,13 +400,17 @@ module('Acceptance | participate in a poll', function(hooks) {
|
|||
});
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`);
|
||||
await visit(
|
||||
`/poll/${poll.id}/participation?encryptionKey=${encryptionKey}`
|
||||
);
|
||||
pollParticipate('John Doe', ['yes', 'no']);
|
||||
|
||||
await waitFor('[data-test-button="submit"] .spinner-border', {
|
||||
timeoutMessage: 'timeout while waiting for loading spinner to appear',
|
||||
});
|
||||
assert.dom('.is-invalid').doesNotExist('does not show any validation error');
|
||||
assert
|
||||
.dom('.is-invalid')
|
||||
.doesNotExist('does not show any validation error');
|
||||
|
||||
// resolve promise for test to finish
|
||||
// need to resolve with a valid response cause otherwise Ember Data would throw
|
||||
|
|
|
@ -2,13 +2,14 @@ import { findAll, currentRouteName, find, visit } from '@ember/test-helpers';
|
|||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import { setupIntl, t } from 'ember-intl/test-support';import switchTab from 'croodle/tests/helpers/switch-tab';
|
||||
import { setupIntl, t } from 'ember-intl/test-support';
|
||||
import switchTab from 'croodle/tests/helpers/switch-tab';
|
||||
import PollEvaluationPage from 'croodle/tests/pages/poll/evaluation';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
module('Acceptance | view evaluation', function(hooks) {
|
||||
hooks.beforeEach(function() {
|
||||
module('Acceptance | view evaluation', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'en');
|
||||
});
|
||||
|
||||
|
@ -16,23 +17,27 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
setupIntl(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
test('evaluation summary is not present for poll without participants', async function(assert) {
|
||||
test('evaluation summary is not present for poll without participants', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey
|
||||
encryptionKey,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.participation');
|
||||
|
||||
await switchTab('evaluation');
|
||||
assert.equal(findAll('.tab-content .tab-pane .evaluation-summary').length, 0, 'evaluation summary is not present');
|
||||
assert.equal(
|
||||
findAll('.tab-content .tab-pane .evaluation-summary').length,
|
||||
0,
|
||||
'evaluation summary is not present'
|
||||
);
|
||||
});
|
||||
|
||||
test('evaluation is correct for FindADate (date-only)', async function(assert) {
|
||||
test('evaluation is correct for FindADate (date-only)', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let user1 = this.server.create('user', {
|
||||
id: "1-1",
|
||||
id: '1-1',
|
||||
creationDate: DateTime.local().minus({ months: 8, weeks: 3 }).toISO(),
|
||||
encryptionKey,
|
||||
name: 'Maximilian',
|
||||
|
@ -41,18 +46,18 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
});
|
||||
let user2 = this.server.create('user', {
|
||||
id: "1-2",
|
||||
id: '1-2',
|
||||
creationDate: DateTime.local().minus({ months: 3, weeks: 2 }).toISO(),
|
||||
encryptionKey,
|
||||
name: 'Peter',
|
||||
|
@ -61,15 +66,15 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
label: 'No',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
});
|
||||
let poll = this.server.create('poll', {
|
||||
id: '1',
|
||||
|
@ -78,26 +83,27 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
encryptionKey,
|
||||
options: [
|
||||
{ title: '2015-12-12' },
|
||||
{ title: '2016-01-01' }
|
||||
],
|
||||
users: [user1, user2]
|
||||
options: [{ title: '2015-12-12' }, { title: '2016-01-01' }],
|
||||
users: [user1, user2],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/evaluation?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(findAll('.tab-content .tab-pane .evaluation-summary').length, 1, 'evaluation summary is present');
|
||||
assert.equal(
|
||||
findAll('.tab-content .tab-pane .evaluation-summary').length,
|
||||
1,
|
||||
'evaluation summary is present'
|
||||
);
|
||||
assert.equal(
|
||||
find('.participants').textContent.trim(),
|
||||
t('poll.evaluation.participants', { count: 2 }).toString(),
|
||||
|
@ -111,7 +117,7 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
assert.equal(
|
||||
find('.last-participation').textContent.trim(),
|
||||
t('poll.evaluation.lastParticipation', {
|
||||
ago: '3 months ago'
|
||||
ago: '3 months ago',
|
||||
}).toString(),
|
||||
'shows last participation date'
|
||||
);
|
||||
|
@ -124,37 +130,56 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
|
||||
assert
|
||||
.dom('[data-test-participant="1-1"] [data-test-value-for="name"]')
|
||||
.hasText('Maximilian', 'shows expected name of first participant in participants table');
|
||||
.hasText(
|
||||
'Maximilian',
|
||||
'shows expected name of first participant in participants table'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-participant="1-2"] [data-test-value-for="name"]')
|
||||
.hasText('Peter', 'shows expected name of second participant in participants table');
|
||||
.hasText(
|
||||
'Peter',
|
||||
'shows expected name of second participant in participants table'
|
||||
);
|
||||
|
||||
assert
|
||||
.dom('[data-test-participant="1-1"] [data-test-value-for="2015-12-12"]')
|
||||
.hasText('Yes', 'shows expected selection for first option of first participant');
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for first option of first participant'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-participant="1-1"] [data-test-value-for="2016-01-01"]')
|
||||
.hasText('Yes', 'shows expected selection for second option of first participant');
|
||||
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for second option of first participant'
|
||||
);
|
||||
|
||||
assert
|
||||
.dom('[data-test-participant="1-2"] [data-test-value-for="2015-12-12"]')
|
||||
.hasText('No', 'shows expected selection for first option of second participant');
|
||||
.hasText(
|
||||
'No',
|
||||
'shows expected selection for first option of second participant'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-participant="1-2"] [data-test-value-for="2016-01-01"]')
|
||||
.hasText('Yes', 'shows expected selection for second option of second participant');
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for second option of second participant'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
findAll('[data-test-participant] [data-test-value-for="name"]').map((el) => el.textContent.trim()),
|
||||
findAll('[data-test-participant] [data-test-value-for="name"]').map(
|
||||
(el) => el.textContent.trim()
|
||||
),
|
||||
['Maximilian', 'Peter'],
|
||||
'Participants are ordered as correctly in participants table'
|
||||
);
|
||||
});
|
||||
|
||||
test('evaluation is correct for FindADate (datetime)', async function(assert) {
|
||||
test('evaluation is correct for FindADate (datetime)', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let user1 = this.server.create('user', {
|
||||
id: "1-1",
|
||||
id: '1-1',
|
||||
creationDate: DateTime.local().minus({ months: 8, weeks: 3 }).toISO(),
|
||||
encryptionKey,
|
||||
name: 'Maximilian',
|
||||
|
@ -163,24 +188,24 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
label: 'No',
|
||||
},
|
||||
]
|
||||
],
|
||||
});
|
||||
let user2 = this.server.create('user', {
|
||||
id: "1-2",
|
||||
id: '1-2',
|
||||
creationDate: DateTime.local().minus({ months: 3, weeks: 2 }).toISO(),
|
||||
encryptionKey,
|
||||
name: 'Peter',
|
||||
|
@ -189,21 +214,21 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
label: 'No',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
});
|
||||
let poll = this.server.create('poll', {
|
||||
id: '1',
|
||||
|
@ -212,27 +237,31 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
encryptionKey,
|
||||
options: [
|
||||
{ title: DateTime.fromISO('2015-12-12T06:06').toISO() },
|
||||
{ title: DateTime.fromISO('2015-12-12T12:12').toISO() },
|
||||
{ title: DateTime.fromISO('2016-01-01T18:18').toISO() }
|
||||
{ title: DateTime.fromISO('2016-01-01T18:18').toISO() },
|
||||
],
|
||||
users: [user1, user2]
|
||||
users: [user1, user2],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}/evaluation?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(findAll('.tab-content .tab-pane .evaluation-summary').length, 1, 'evaluation summary is present');
|
||||
assert.equal(
|
||||
findAll('.tab-content .tab-pane .evaluation-summary').length,
|
||||
1,
|
||||
'evaluation summary is present'
|
||||
);
|
||||
assert.equal(
|
||||
find('.participants').textContent.trim(),
|
||||
t('poll.evaluation.participants', { count: 2 }).toString(),
|
||||
|
@ -246,58 +275,111 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
assert.equal(
|
||||
find('.last-participation').textContent.trim(),
|
||||
t('poll.evaluation.lastParticipation', {
|
||||
ago: '3 months ago'
|
||||
ago: '3 months ago',
|
||||
}).toString(),
|
||||
'shows last participation date'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
findAll('table thead tr:first-child th').map((el) => el.textContent.trim()),
|
||||
findAll('table thead tr:first-child th').map((el) =>
|
||||
el.textContent.trim()
|
||||
),
|
||||
['', 'Saturday, December 12, 2015', 'Friday, January 1, 2016'],
|
||||
'lists days as first row in table header of parcipants table'
|
||||
);
|
||||
assert.deepEqual(
|
||||
findAll('table thead tr:last-child th').map((el) => el.textContent.trim()),
|
||||
findAll('table thead tr:last-child th').map((el) =>
|
||||
el.textContent.trim()
|
||||
),
|
||||
['', '6:06 AM', '12:12 PM', '6:18 PM'],
|
||||
'lists times as second row in table header of parcipants table'
|
||||
);
|
||||
|
||||
assert
|
||||
.dom('[data-test-participant="1-1"] [data-test-value-for="name"]')
|
||||
.hasText('Maximilian', 'shows expected name of first participant in participants table');
|
||||
.hasText(
|
||||
'Maximilian',
|
||||
'shows expected name of first participant in participants table'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-participant="1-2"] [data-test-value-for="name"]')
|
||||
.hasText('Peter', 'shows expected name of second participant in participants table');
|
||||
.hasText(
|
||||
'Peter',
|
||||
'shows expected name of second participant in participants table'
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO('2015-12-12T06:06').toISO()}"]`)
|
||||
.hasText('Yes', 'shows expected selection for first option of first participant');
|
||||
.dom(
|
||||
`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2015-12-12T06:06'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for first option of first participant'
|
||||
);
|
||||
assert
|
||||
.dom(`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO('2015-12-12T12:12').toISO()}"]`)
|
||||
.hasText('Yes', 'shows expected selection for second option of first participant');
|
||||
.dom(
|
||||
`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2015-12-12T12:12'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for second option of first participant'
|
||||
);
|
||||
assert
|
||||
.dom(`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO('2016-01-01T18:18').toISO()}"]`)
|
||||
.hasText('No', 'shows expected selection for third option of first participant');
|
||||
|
||||
.dom(
|
||||
`[data-test-participant="1-1"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2016-01-01T18:18'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'No',
|
||||
'shows expected selection for third option of first participant'
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO('2015-12-12T06:06').toISO()}"]`)
|
||||
.hasText('No', 'shows expected selection for first option of second participant');
|
||||
.dom(
|
||||
`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2015-12-12T06:06'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'No',
|
||||
'shows expected selection for first option of second participant'
|
||||
);
|
||||
assert
|
||||
.dom(`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO('2015-12-12T12:12').toISO()}"]`)
|
||||
.hasText('Yes', 'shows expected selection for second option of second participant');
|
||||
.dom(
|
||||
`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2015-12-12T12:12'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for second option of second participant'
|
||||
);
|
||||
assert
|
||||
.dom(`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO('2016-01-01T18:18').toISO()}"]`)
|
||||
.hasText('Yes', 'shows expected selection for third option of second participant');
|
||||
.dom(
|
||||
`[data-test-participant="1-2"] [data-test-value-for="${DateTime.fromISO(
|
||||
'2016-01-01T18:18'
|
||||
).toISO()}"]`
|
||||
)
|
||||
.hasText(
|
||||
'Yes',
|
||||
'shows expected selection for third option of second participant'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
findAll('[data-test-participant] [data-test-value-for="name"]').map((el) => el.textContent.trim()),
|
||||
findAll('[data-test-participant] [data-test-value-for="name"]').map(
|
||||
(el) => el.textContent.trim()
|
||||
),
|
||||
['Maximilian', 'Peter'],
|
||||
'Participants are ordered as correctly in participants table'
|
||||
);
|
||||
});
|
||||
|
||||
test('evaluation is correct for MakeAPoll', async function(assert) {
|
||||
test('evaluation is correct for MakeAPoll', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let usersData = [
|
||||
{
|
||||
|
@ -309,15 +391,15 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
creationDate: DateTime.local().minus({ days: 3 }).toISO(),
|
||||
|
@ -328,15 +410,15 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
label: 'No',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
let pollData = {
|
||||
|
@ -345,27 +427,33 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
encryptionKey,
|
||||
options: [
|
||||
{ title: 'first option' },
|
||||
{ title: 'second option' }
|
||||
],
|
||||
options: [{ title: 'first option' }, { title: 'second option' }],
|
||||
pollType: 'MakeAPoll',
|
||||
};
|
||||
let poll = this.server.create('poll', assign(pollData, { users: usersData.map((_) => this.server.create('user', _)) }));
|
||||
let poll = this.server.create(
|
||||
'poll',
|
||||
assign(pollData, {
|
||||
users: usersData.map((_) => this.server.create('user', _)),
|
||||
})
|
||||
);
|
||||
|
||||
await visit(`/poll/${poll.id}/evaluation?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentRouteName(), 'poll.evaluation');
|
||||
assert.equal(findAll('.tab-content .tab-pane .evaluation-summary').length, 1, 'evaluation summary is present');
|
||||
assert.equal(
|
||||
findAll('.tab-content .tab-pane .evaluation-summary').length,
|
||||
1,
|
||||
'evaluation summary is present'
|
||||
);
|
||||
assert.equal(
|
||||
find('.participants').textContent.trim(),
|
||||
t('poll.evaluation.participants', { count: 2 }).toString(),
|
||||
|
@ -383,11 +471,15 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
'dates are used as table headers'
|
||||
);
|
||||
assert.deepEqual(
|
||||
PollEvaluationPage.participants.map((_) => _.name), usersData.map((_) => _.name),
|
||||
PollEvaluationPage.participants.map((_) => _.name),
|
||||
usersData.map((_) => _.name),
|
||||
'users are listed in participants table with their names'
|
||||
);
|
||||
usersData.forEach((user) => {
|
||||
let participant = PollEvaluationPage.participants.filterBy('name', user.name)[0];
|
||||
let participant = PollEvaluationPage.participants.filterBy(
|
||||
'name',
|
||||
user.name
|
||||
)[0];
|
||||
assert.deepEqual(
|
||||
participant.selections.map((_) => _.answer),
|
||||
user.selections.map((_) => t(_.labelTranslation).toString()),
|
||||
|
@ -398,13 +490,13 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
assert.equal(
|
||||
find('.last-participation').textContent.trim(),
|
||||
t('poll.evaluation.lastParticipation', {
|
||||
ago: '3 days ago'
|
||||
ago: '3 days ago',
|
||||
}).toString(),
|
||||
'last participation is evaluated correctly'
|
||||
);
|
||||
});
|
||||
|
||||
test('could open evaluation by tab from poll participation', async function(assert) {
|
||||
test('could open evaluation by tab from poll participation', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
answers: [
|
||||
|
@ -412,20 +504,17 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
encryptionKey,
|
||||
options: [
|
||||
{ title: '2015-12-12' },
|
||||
{ title: '2016-01-01' }
|
||||
],
|
||||
options: [{ title: '2015-12-12' }, { title: '2016-01-01' }],
|
||||
users: [
|
||||
this.server.create('user', {
|
||||
creationDate: '2015-01-01T00:00:00.000Z',
|
||||
|
@ -436,15 +525,15 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
}),
|
||||
this.server.create('user', {
|
||||
creationDate: '2015-08-01T00:00:00.000Z',
|
||||
|
@ -455,17 +544,17 @@ module('Acceptance | view evaluation', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
id: 'no',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
label: 'Yes',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { click, currentURL, currentRouteName, visit } from '@ember/test-helpers';
|
||||
import {
|
||||
click,
|
||||
currentURL,
|
||||
currentRouteName,
|
||||
visit,
|
||||
} from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
@ -8,15 +13,15 @@ import pageEvaluation from 'croodle/tests/pages/poll/evaluation';
|
|||
import { DateTime } from 'luxon';
|
||||
import { triggerCopySuccess } from 'ember-cli-clipboard/test-support';
|
||||
|
||||
module('Acceptance | view poll', function(hooks) {
|
||||
hooks.beforeEach(function() {
|
||||
module('Acceptance | view poll', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
window.localStorage.setItem('locale', 'en');
|
||||
});
|
||||
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
test('poll url', async function(assert) {
|
||||
test('poll url', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz012345789';
|
||||
let poll = this.server.create('poll', { encryptionKey });
|
||||
let pollUrl = `/poll/${poll.id}?encryptionKey=${encryptionKey}`;
|
||||
|
@ -35,10 +40,10 @@ module('Acceptance | view poll', function(hooks) {
|
|||
*
|
||||
* Can't test if flash message is shown due to
|
||||
* https://github.com/poteto/ember-cli-flash/issues/202
|
||||
*/
|
||||
*/
|
||||
});
|
||||
|
||||
test('shows a warning if poll is about to be expired', async function(assert) {
|
||||
test('shows a warning if poll is about to be expired', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
|
@ -46,34 +51,26 @@ module('Acceptance | view poll', function(hooks) {
|
|||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.ok(
|
||||
pageParticipation.showsExpirationWarning
|
||||
);
|
||||
assert.ok(pageParticipation.showsExpirationWarning);
|
||||
});
|
||||
|
||||
test('view a poll with dates', async function(assert) {
|
||||
test('view a poll with dates', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
options: [
|
||||
{ title: '2015-12-12' },
|
||||
{ title: '2016-01-01' }
|
||||
]
|
||||
options: [{ title: '2015-12-12' }, { title: '2016-01-01' }],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.deepEqual(
|
||||
pageParticipation.options().labels,
|
||||
[
|
||||
'Saturday, December 12, 2015',
|
||||
'Friday, January 1, 2016'
|
||||
]
|
||||
);
|
||||
assert.deepEqual(pageParticipation.options().labels, [
|
||||
'Saturday, December 12, 2015',
|
||||
'Friday, January 1, 2016',
|
||||
]);
|
||||
});
|
||||
|
||||
test('view a poll with dates and times', async function(assert) {
|
||||
test('view a poll with dates and times', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ;
|
||||
let timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
expirationDate: DateTime.local().plus({ years: 1 }).toISO(),
|
||||
|
@ -84,39 +81,37 @@ module('Acceptance | view poll', function(hooks) {
|
|||
// time zone as UTC rather than local time
|
||||
{ title: DateTime.fromISO('2015-12-12T11:11:00').toISO() },
|
||||
{ title: DateTime.fromISO('2015-12-12T13:13:00').toISO() },
|
||||
{ title: DateTime.fromISO('2016-01-01T11:11:00').toISO() }
|
||||
{ title: DateTime.fromISO('2016-01-01T11:11:00').toISO() },
|
||||
],
|
||||
timezone
|
||||
timezone,
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.deepEqual(
|
||||
pageParticipation.options().labels,
|
||||
[
|
||||
// full date
|
||||
'Saturday, December 12, 2015 at 11:11 AM',
|
||||
// only time cause day is repeated
|
||||
'1:13 PM',
|
||||
// full date cause day changed
|
||||
'Friday, January 1, 2016 at 11:11 AM',
|
||||
]
|
||||
);
|
||||
assert.deepEqual(pageParticipation.options().labels, [
|
||||
// full date
|
||||
'Saturday, December 12, 2015 at 11:11 AM',
|
||||
// only time cause day is repeated
|
||||
'1:13 PM',
|
||||
// full date cause day changed
|
||||
'Friday, January 1, 2016 at 11:11 AM',
|
||||
]);
|
||||
assert.notOk(
|
||||
pageParticipation.showsExpirationWarning,
|
||||
'does not show an expiration warning if poll will not expire in next weeks'
|
||||
);
|
||||
});
|
||||
|
||||
test('view a poll while timezone differs from the one poll got created in and choose local timezone', async function(assert) {
|
||||
test('view a poll while timezone differs from the one poll got created in and choose local timezone', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let timezoneUser = Intl.DateTimeFormat().resolvedOptions().timeZone ;
|
||||
let timezonePoll = timezoneUser !== 'America/Caracas' ? 'America/Caracas' : 'Europe/Moscow';
|
||||
let timezoneUser = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
let timezonePoll =
|
||||
timezoneUser !== 'America/Caracas' ? 'America/Caracas' : 'Europe/Moscow';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
isDateTime: true,
|
||||
options: [
|
||||
{ title: '2015-12-12T11:11:00.000Z' },
|
||||
{ title: '2016-01-01T11:11:00.000Z' }
|
||||
{ title: '2016-01-01T11:11:00.000Z' },
|
||||
],
|
||||
timezone: timezonePoll,
|
||||
users: [
|
||||
|
@ -127,50 +122,61 @@ module('Acceptance | view poll', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.dom('[data-test-modal="choose-timezone"]')
|
||||
assert
|
||||
.dom('[data-test-modal="choose-timezone"]')
|
||||
.exists('user is asked which timezone should be used');
|
||||
|
||||
await click('[data-test-modal="choose-timezone"] [data-test-button="use-local-timezone"]');
|
||||
assert.deepEqual(
|
||||
pageParticipation.options().labels,
|
||||
[
|
||||
Intl.DateTimeFormat('en-US', { dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', { dateStyle: "full", timeStyle: "short" }).format(new Date('2016-01-01T11:11:00.000Z')),
|
||||
]
|
||||
await click(
|
||||
'[data-test-modal="choose-timezone"] [data-test-button="use-local-timezone"]'
|
||||
);
|
||||
assert.dom('[data-test-modal="choose-timezone"]').doesNotExist('modal is closed');
|
||||
assert.deepEqual(pageParticipation.options().labels, [
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2016-01-01T11:11:00.000Z')),
|
||||
]);
|
||||
assert
|
||||
.dom('[data-test-modal="choose-timezone"]')
|
||||
.doesNotExist('modal is closed');
|
||||
|
||||
await switchTab('evaluation');
|
||||
assert.deepEqual(
|
||||
pageEvaluation.preferedOptions,
|
||||
[Intl.DateTimeFormat('en-US', { dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-12T11:11:00.000Z'))]
|
||||
);
|
||||
assert.deepEqual(pageEvaluation.preferedOptions, [
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
]);
|
||||
});
|
||||
|
||||
test('view a poll while timezone differs from the one poll got created in and choose poll timezone', async function(assert) {
|
||||
test('view a poll while timezone differs from the one poll got created in and choose poll timezone', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let timezoneUser = Intl.DateTimeFormat().resolvedOptions().timeZone ;
|
||||
let timezonePoll = timezoneUser !== 'America/Caracas' ? 'America/Caracas' : 'Europe/Moscow';
|
||||
let timezoneUser = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
let timezonePoll =
|
||||
timezoneUser !== 'America/Caracas' ? 'America/Caracas' : 'Europe/Moscow';
|
||||
let poll = this.server.create('poll', {
|
||||
encryptionKey,
|
||||
isDateTime: true,
|
||||
options: [
|
||||
{ title: '2015-12-12T11:11:00.000Z' },
|
||||
{ title: '2016-01-01T11:11:00.000Z' }
|
||||
{ title: '2016-01-01T11:11:00.000Z' },
|
||||
],
|
||||
timezone: timezonePoll,
|
||||
users: [
|
||||
|
@ -181,61 +187,94 @@ module('Acceptance | view poll', function(hooks) {
|
|||
type: 'yes',
|
||||
labelTranslation: 'answerTypes.yes.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-up',
|
||||
label: 'Yes'
|
||||
label: 'Yes',
|
||||
},
|
||||
{
|
||||
type: 'no',
|
||||
labelTranslation: 'answerTypes.no.label',
|
||||
icon: 'glyphicon glyphicon-thumbs-down',
|
||||
label: 'No'
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
label: 'No',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.dom('[data-test-modal="choose-timezone"]')
|
||||
assert
|
||||
.dom('[data-test-modal="choose-timezone"]')
|
||||
.exists('user is asked which timezone should be used');
|
||||
|
||||
await click('[data-test-modal="choose-timezone"] [data-test-button="use-poll-timezone"]');
|
||||
assert.deepEqual(
|
||||
pageParticipation.options().labels,
|
||||
[
|
||||
Intl.DateTimeFormat('en-US', { timeZone: timezonePoll, dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', { timeZone: timezonePoll, dateStyle: "full", timeStyle: "short" }).format(new Date('2016-01-01T11:11:00.000Z')),
|
||||
]
|
||||
await click(
|
||||
'[data-test-modal="choose-timezone"] [data-test-button="use-poll-timezone"]'
|
||||
);
|
||||
assert.dom('[data-test-modal="choose-timezone"]').doesNotExist('modal is closed');
|
||||
assert.deepEqual(pageParticipation.options().labels, [
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
timeZone: timezonePoll,
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
timeZone: timezonePoll,
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2016-01-01T11:11:00.000Z')),
|
||||
]);
|
||||
assert
|
||||
.dom('[data-test-modal="choose-timezone"]')
|
||||
.doesNotExist('modal is closed');
|
||||
|
||||
await switchTab('evaluation');
|
||||
assert.deepEqual(
|
||||
pageEvaluation.preferedOptions,
|
||||
[Intl.DateTimeFormat('en-US', { timeZone: timezonePoll, dateStyle: "full", timeStyle: "short" }).format(new Date('2015-12-12T11:11:00.000Z')),]
|
||||
);
|
||||
assert.deepEqual(pageEvaluation.preferedOptions, [
|
||||
Intl.DateTimeFormat('en-US', {
|
||||
timeZone: timezonePoll,
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date('2015-12-12T11:11:00.000Z')),
|
||||
]);
|
||||
});
|
||||
|
||||
test('shows error page if poll does not exist', async function(assert) {
|
||||
test('shows error page if poll does not exist', async function (assert) {
|
||||
let pollId = 'not-existing';
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
await visit(`/poll/${pollId}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentURL(), `/poll/${pollId}?encryptionKey=${encryptionKey}`, 'shows URL entered by user');
|
||||
assert.equal(currentRouteName(), 'poll_error', 'shows error substate of poll route');
|
||||
assert.dom('[data-test-error-type]').hasAttribute('data-test-error-type', 'not-found');
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
`/poll/${pollId}?encryptionKey=${encryptionKey}`,
|
||||
'shows URL entered by user'
|
||||
);
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll_error',
|
||||
'shows error substate of poll route'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-error-type]')
|
||||
.hasAttribute('data-test-error-type', 'not-found');
|
||||
});
|
||||
|
||||
test('shows error page if encryption key is wrong', async function(assert) {
|
||||
test('shows error page if encryption key is wrong', async function (assert) {
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let poll = this.server.create('poll', { encryptionKey: 'anotherkey' });
|
||||
|
||||
await visit(`/poll/${poll.id}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentURL(), `/poll/${poll.id}?encryptionKey=${encryptionKey}`, 'shows URL entered by user');
|
||||
assert.equal(currentRouteName(), 'poll_error', 'shows error substate of poll route');
|
||||
assert.dom('[data-test-error-type]').hasAttribute('data-test-error-type', 'decryption-failed');
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
`/poll/${poll.id}?encryptionKey=${encryptionKey}`,
|
||||
'shows URL entered by user'
|
||||
);
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll_error',
|
||||
'shows error substate of poll route'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-error-type]')
|
||||
.hasAttribute('data-test-error-type', 'decryption-failed');
|
||||
});
|
||||
|
||||
test('shows error page if server returns a 500', async function(assert) {
|
||||
test('shows error page if server returns a 500', async function (assert) {
|
||||
let pollId = 'not-existing';
|
||||
let encryptionKey = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
|
@ -243,8 +282,18 @@ module('Acceptance | view poll', function(hooks) {
|
|||
this.server.get('polls/:id', () => {}, 500);
|
||||
|
||||
await visit(`/poll/${pollId}?encryptionKey=${encryptionKey}`);
|
||||
assert.equal(currentURL(), `/poll/${pollId}?encryptionKey=${encryptionKey}`, 'shows URL entered by user');
|
||||
assert.equal(currentRouteName(), 'poll_error', 'shows error substate of poll route');
|
||||
assert.dom('[data-test-error-type]').hasAttribute('data-test-error-type', 'unexpected');
|
||||
})
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
`/poll/${pollId}?encryptionKey=${encryptionKey}`,
|
||||
'shows URL entered by user'
|
||||
);
|
||||
assert.equal(
|
||||
currentRouteName(),
|
||||
'poll_error',
|
||||
'shows error substate of poll route'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-error-type]')
|
||||
.hasAttribute('data-test-error-type', 'unexpected');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -47,14 +47,14 @@ export default async function asyncThrows(f, expectedErrorMessage) {
|
|||
result,
|
||||
expected: expectedErrorMessage,
|
||||
actual: errorText,
|
||||
message: `Expected to see error '${expectedErrorMessage}'`
|
||||
message: `Expected to see error '${expectedErrorMessage}'`,
|
||||
});
|
||||
} else {
|
||||
this.pushResult({
|
||||
result: false,
|
||||
expected: '',
|
||||
actual: errorText,
|
||||
message: `You're using asyncThrows but you didn't add text to the assertion. Add some text as the second argument so the actual exception being thrown is what you expect it is.`
|
||||
message: `You're using asyncThrows but you didn't add text to the assertion. Add some text as the second argument so the actual exception being thrown is what you expect it is.`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,7 @@ function pollHasUsersCount(assert, count, message) {
|
|||
if (isEmpty(message)) {
|
||||
message = 'poll has expected count of users';
|
||||
}
|
||||
assert.equal(
|
||||
findAll('.user').length,
|
||||
count,
|
||||
message
|
||||
);
|
||||
assert.equal(findAll('.user').length, count, message);
|
||||
}
|
||||
|
||||
export default pollHasUser;
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
import { isEmpty } from '@ember/utils';
|
||||
import { findAll, fillIn, click, settled } from '@ember/test-helpers';
|
||||
import { findAll, fillIn, click } from '@ember/test-helpers';
|
||||
|
||||
export default async function(name, selections) {
|
||||
export default async function (name, selections) {
|
||||
if (!isEmpty(name)) {
|
||||
await fillIn('.participation .name input', name);
|
||||
}
|
||||
|
||||
const isFreeText = findAll('.participation .selections .radio').length > 0 ? false : true;
|
||||
const isFreeText =
|
||||
findAll('.participation .selections .radio').length > 0 ? false : true;
|
||||
for (let [index, selection] of selections.entries()) {
|
||||
if (!isEmpty(selection)) {
|
||||
if (isFreeText) {
|
||||
await fillIn(`.participation .selections .form-group:nth-child(${index + 1}) input`, selection);
|
||||
await fillIn(
|
||||
`.participation .selections .form-group:nth-child(${
|
||||
index + 1
|
||||
}) input`,
|
||||
selection
|
||||
);
|
||||
} else {
|
||||
await click(`.participation .selections .form-group:nth-child(${index + 1}) .${selection}.radio input`);
|
||||
await click(
|
||||
`.participation .selections .form-group:nth-child(${
|
||||
index + 1
|
||||
}) .${selection}.radio input`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await click('.participation button[type="submit"]');
|
||||
|
||||
await settled();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { click } from '@ember/test-helpers';
|
||||
|
||||
export default function(tab) {
|
||||
export default function (tab) {
|
||||
return click(`.nav-tabs .${tab} a`);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,14 @@
|
|||
{{content-for "body"}}
|
||||
{{content-for "test-body"}}
|
||||
|
||||
<script src="/testem.js" integrity=""></script>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture">
|
||||
<div id="ember-testing-container">
|
||||
<div id="ember-testing"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/testem.js" integrity="" data-embroider-ignore></script>
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/test-support.js"></script>
|
||||
<script src="{{rootURL}}assets/croodle.js"></script>
|
||||
|
|
|
@ -3,10 +3,10 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | back-button', function(hooks) {
|
||||
module('Integration | Component | back-button', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders a button', async function(assert) {
|
||||
test('it renders a button', async function (assert) {
|
||||
await render(hbs`<BackButton />`);
|
||||
|
||||
assert.dom('button').exists();
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { run } from '@ember/runloop';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, click, find, findAll } from "@ember/test-helpers";
|
||||
import { render, click, find, findAll } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
module('Integration | Component | create options datetime', function(hooks) {
|
||||
module('Integration | Component | create options datetime', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
|
@ -19,18 +19,19 @@ module('Integration | Component | create options datetime', function(hooks) {
|
|||
* that ones could be identifed by class 'ws-inputreplace'
|
||||
*/
|
||||
|
||||
test('it generates input field for options iso 8601 date string (without time)', async function(assert) {
|
||||
test('it generates input field for options iso 8601 date string (without time)', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
// it's owner
|
||||
run(() => {
|
||||
this.set('poll', this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: '2015-01-01' }
|
||||
]
|
||||
}));
|
||||
this.set(
|
||||
'poll',
|
||||
this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [{ title: '2015-01-01' }],
|
||||
})
|
||||
);
|
||||
});
|
||||
await render(hbs`{{create-options-datetime dates=poll.options}}`);
|
||||
|
||||
|
@ -46,18 +47,19 @@ module('Integration | Component | create options datetime', function(hooks) {
|
|||
);
|
||||
});
|
||||
|
||||
test('it generates input field for options iso 8601 datetime string (with time)', async function(assert) {
|
||||
test('it generates input field for options iso 8601 datetime string (with time)', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
// it's owner
|
||||
run(() => {
|
||||
this.set('poll', this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: '2015-01-01T11:11:00.000Z' }
|
||||
]
|
||||
}));
|
||||
this.set(
|
||||
'poll',
|
||||
this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [{ title: '2015-01-01T11:11:00.000Z' }],
|
||||
})
|
||||
);
|
||||
});
|
||||
await render(hbs`{{create-options-datetime dates=poll.options}}`);
|
||||
|
||||
|
@ -73,20 +75,23 @@ module('Integration | Component | create options datetime', function(hooks) {
|
|||
);
|
||||
});
|
||||
|
||||
test('it hides repeated labels', async function(assert) {
|
||||
test('it hides repeated labels', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
// it's owner
|
||||
run(() => {
|
||||
this.set('poll', this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: DateTime.fromISO('2015-01-01T10:11').toISO() },
|
||||
{ title: DateTime.fromISO('2015-01-01T22:22').toISO() },
|
||||
{ title: '2015-02-02' }
|
||||
]
|
||||
}));
|
||||
this.set(
|
||||
'poll',
|
||||
this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: DateTime.fromISO('2015-01-01T10:11').toISO() },
|
||||
{ title: DateTime.fromISO('2015-01-01T22:22').toISO() },
|
||||
{ title: '2015-02-02' },
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
await render(hbs`{{create-options-datetime dates=poll.options}}`);
|
||||
|
||||
|
@ -101,31 +106,37 @@ module('Integration | Component | create options datetime', function(hooks) {
|
|||
'there are two not hidden labels for two different dates'
|
||||
);
|
||||
assert.notOk(
|
||||
findAll('.days .form-group')[0].querySelector('label').classList.contains('sr-only'),
|
||||
findAll('.days .form-group')[0]
|
||||
.querySelector('label')
|
||||
.classList.contains('sr-only'),
|
||||
'the first label is shown'
|
||||
);
|
||||
assert.ok(
|
||||
findAll('.days .form-group')[1].querySelector('label').classList.contains('sr-only'),
|
||||
findAll('.days .form-group')[1]
|
||||
.querySelector('label')
|
||||
.classList.contains('sr-only'),
|
||||
'the repeated label on second form-group is hidden by sr-only class'
|
||||
);
|
||||
assert.notOk(
|
||||
findAll('.days .form-group')[2].querySelector('label').classList.contains('sr-only'),
|
||||
findAll('.days .form-group')[2]
|
||||
.querySelector('label')
|
||||
.classList.contains('sr-only'),
|
||||
'the new label on third form-group is shown'
|
||||
);
|
||||
});
|
||||
|
||||
test('allows to add another option', async function(assert) {
|
||||
test('allows to add another option', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
// it's owner
|
||||
run(() => {
|
||||
this.set('poll', this.store.createRecord('poll', {
|
||||
options: [
|
||||
{ title: '2015-01-01' },
|
||||
{ title: '2015-02-02' }
|
||||
]
|
||||
}));
|
||||
this.set(
|
||||
'poll',
|
||||
this.store.createRecord('poll', {
|
||||
options: [{ title: '2015-01-01' }, { title: '2015-02-02' }],
|
||||
})
|
||||
);
|
||||
});
|
||||
await render(hbs`{{create-options-datetime dates=poll.options}}`);
|
||||
|
||||
|
@ -147,24 +158,29 @@ module('Integration | Component | create options datetime', function(hooks) {
|
|||
'new input has correct label'
|
||||
);
|
||||
assert.ok(
|
||||
findAll('.days .form-group')[1].querySelector('label').classList.contains('sr-only'),
|
||||
'label ofnew input is hidden cause it\'s repeated'
|
||||
findAll('.days .form-group')[1]
|
||||
.querySelector('label')
|
||||
.classList.contains('sr-only'),
|
||||
"label ofnew input is hidden cause it's repeated"
|
||||
);
|
||||
});
|
||||
|
||||
test('allows to delete an option', async function(assert) {
|
||||
test('allows to delete an option', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
// it's owner
|
||||
run(() => {
|
||||
this.set('poll', this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: DateTime.fromISO('2015-01-01T11:11').toISO() },
|
||||
{ title: DateTime.fromISO('2015-01-01T22:22').toISO() }
|
||||
]
|
||||
}));
|
||||
this.set(
|
||||
'poll',
|
||||
this.store.createRecord('poll', {
|
||||
pollType: 'FindADate',
|
||||
options: [
|
||||
{ title: DateTime.fromISO('2015-01-01T11:11').toISO() },
|
||||
{ title: DateTime.fromISO('2015-01-01T22:22').toISO() },
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
await render(hbs`<CreateOptionsDatetime @dates={{this.poll.options}} />`);
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render, findAll, blur, fillIn, focus } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | create options', function(hooks) {
|
||||
module('Integration | Component | create options', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
test('shows validation errors if options are not unique (makeAPoll)', async function(assert) {
|
||||
test('shows validation errors if options are not unique (makeAPoll)', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
// validation is based on validation of every option fragment
|
||||
|
@ -36,26 +36,34 @@ module('Integration | Component | create options', function(hooks) {
|
|||
/>
|
||||
`);
|
||||
|
||||
assert.dom('.form-group').exists({ count: 2 }, 'assumption: renders two form groups');
|
||||
assert
|
||||
.dom('.form-group')
|
||||
.exists({ count: 2 }, 'assumption: renders two form groups');
|
||||
|
||||
await fillIn('.form-group:nth-child(1) input', 'foo');
|
||||
await blur('.form-group:nth-child(1) input');
|
||||
await fillIn('.form-group:nth-child(2) input', 'foo');
|
||||
await blur('.form-group:nth-child(2) input');
|
||||
assert.dom('.form-group:nth-child(2) input')
|
||||
assert
|
||||
.dom('.form-group:nth-child(2) input')
|
||||
.hasClass('is-invalid', 'second input field has validation error');
|
||||
assert.dom('.form-group:nth-child(2) .invalid-feedback')
|
||||
assert
|
||||
.dom('.form-group:nth-child(2) .invalid-feedback')
|
||||
.exists('validation error is shown');
|
||||
|
||||
await fillIn(findAll('input')[0], 'bar');
|
||||
await blur(findAll('input')[0]);
|
||||
assert.dom('.form-group .invalid-feedback')
|
||||
.doesNotExist('there is no validation error anymore after a unique value is entered');
|
||||
assert.dom('.form-group .is-invalid')
|
||||
assert
|
||||
.dom('.form-group .invalid-feedback')
|
||||
.doesNotExist(
|
||||
'there is no validation error anymore after a unique value is entered'
|
||||
);
|
||||
assert
|
||||
.dom('.form-group .is-invalid')
|
||||
.doesNotExist('.is-invalid classes are removed');
|
||||
});
|
||||
|
||||
test('shows validation errors if option is empty (makeAPoll)', async function(assert) {
|
||||
test('shows validation errors if option is empty (makeAPoll)', async function (assert) {
|
||||
// validation is based on validation of every option fragment
|
||||
// which validates according to poll model it belongs to
|
||||
// therefore each option needs to be pushed to poll model to have it as
|
||||
|
@ -78,9 +86,7 @@ module('Integration | Component | create options', function(hooks) {
|
|||
/>
|
||||
`);
|
||||
|
||||
assert.equal(
|
||||
findAll('.form-group.has-error').length, 0
|
||||
);
|
||||
assert.equal(findAll('.form-group.has-error').length, 0);
|
||||
|
||||
await focus(findAll('input')[0]);
|
||||
await blur(findAll('input')[0]);
|
||||
|
|
|
@ -3,10 +3,10 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | inline-datepicker', function(hooks) {
|
||||
module('Integration | Component | inline-datepicker', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders an ember-power-calendar', async function(assert) {
|
||||
test('it renders an ember-power-calendar', async function (assert) {
|
||||
this.set('noop', () => {});
|
||||
await render(hbs`{{inline-datepicker onCenterChange=noop onSelect=noop}}`);
|
||||
|
||||
|
|
|
@ -3,17 +3,17 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | next-button', function(hooks) {
|
||||
module('Integration | Component | next-button', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders a button', async function(assert) {
|
||||
test('it renders a button', async function (assert) {
|
||||
await render(hbs`<NextButton />`);
|
||||
|
||||
assert.dom('button').exists();
|
||||
assert.dom('button').hasAttribute('type', 'submit');
|
||||
});
|
||||
|
||||
test('it renders a loading spinner if `@isPending` is `true`', async function(assert) {
|
||||
test('it renders a loading spinner if `@isPending` is `true`', async function (assert) {
|
||||
await render(hbs`<NextButton @isPending={{true}} />`);
|
||||
|
||||
assert.dom('button .spinner-border').exists();
|
||||
|
|
|
@ -3,17 +3,17 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | save-button', function(hooks) {
|
||||
module('Integration | Component | save-button', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders a button', async function(assert) {
|
||||
test('it renders a button', async function (assert) {
|
||||
await render(hbs`<NextButton />`);
|
||||
|
||||
assert.dom('button').exists();
|
||||
assert.dom('button').hasAttribute('type', 'submit');
|
||||
});
|
||||
|
||||
test('it renders a loading spinner if `@isPending` is `true`', async function(assert) {
|
||||
test('it renders a loading spinner if `@isPending` is `true`', async function (assert) {
|
||||
await render(hbs`<NextButton @isPending={{true}} />`);
|
||||
|
||||
assert.dom('button .spinner-border').exists();
|
||||
|
|
|
@ -4,10 +4,10 @@ import { render } from '@ember/test-helpers';
|
|||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
module('Integration | Helper | format-date-relative', function(hooks) {
|
||||
module('Integration | Helper | format-date-relative', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it formats an ISO date to relative duration from now', async function(assert) {
|
||||
test('it formats an ISO date to relative duration from now', async function (assert) {
|
||||
this.set('date', DateTime.local().plus({ hours: 6 }));
|
||||
|
||||
await render(hbs`{{format-date-relative this.date}}`);
|
||||
|
@ -35,7 +35,7 @@ module('Integration | Helper | format-date-relative', function(hooks) {
|
|||
assert.dom(this.element).hasText('in 1 year');
|
||||
});
|
||||
|
||||
test('it formats an ISO date to relative duration to now', async function(assert) {
|
||||
test('it formats an ISO date to relative duration to now', async function (assert) {
|
||||
this.set('date', DateTime.local().minus({ hours: 6 }));
|
||||
|
||||
await render(hbs`{{format-date-relative this.date}}`);
|
||||
|
|
|
@ -3,11 +3,11 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Helper | mark-as-safe-html', function(hooks) {
|
||||
module('Integration | Helper | mark-as-safe-html', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it renders', async function(assert) {
|
||||
test('it renders', async function (assert) {
|
||||
this.set('inputValue', '1234');
|
||||
|
||||
await render(hbs`{{mark-as-safe-html "<p>foo</p>"}}`);
|
||||
|
|
|
@ -3,15 +3,18 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Helper | scroll-first-invalid-element-into-view-port', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
module(
|
||||
'Integration | Helper | scroll-first-invalid-element-into-view-port',
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it renders', async function(assert) {
|
||||
this.set('inputValue', '1234');
|
||||
// Replace this with your real tests.
|
||||
test('it renders', async function (assert) {
|
||||
this.set('inputValue', '1234');
|
||||
|
||||
await render(hbs`{{scroll-element-into-view-port inputValue}}`);
|
||||
await render(hbs`{{scroll-element-into-view-port inputValue}}`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '1234');
|
||||
});
|
||||
});
|
||||
assert.equal(this.element.textContent.trim(), '1234');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,34 +1,36 @@
|
|||
import { get } from '@ember/object';
|
||||
import { module, test } from 'qunit';
|
||||
import { startMirage } from 'croodle/initializers/ember-cli-mirage';
|
||||
import sjcl from 'sjcl';
|
||||
|
||||
module('Integration | Mirage api mocking', function(hooks) {
|
||||
hooks.beforeEach(function() {
|
||||
module('Integration | Mirage api mocking', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.server = startMirage();
|
||||
});
|
||||
|
||||
hooks.afterEach(function() {
|
||||
hooks.afterEach(function () {
|
||||
this.server.shutdown();
|
||||
});
|
||||
|
||||
test('poll factory | encrypts properties', function(assert) {
|
||||
test('poll factory | encrypts properties', function (assert) {
|
||||
let encryptionKey = 'abc';
|
||||
let poll = this.server.create('poll', {
|
||||
description: 'bar',
|
||||
encryptionKey,
|
||||
title: 'foo'
|
||||
title: 'foo',
|
||||
});
|
||||
assert.equal(JSON.parse(sjcl.decrypt(encryptionKey, get(poll, 'title'))), 'foo');
|
||||
assert.equal(JSON.parse(sjcl.decrypt(encryptionKey, get(poll, 'description'))), 'bar');
|
||||
assert.equal(JSON.parse(sjcl.decrypt(encryptionKey, poll.title)), 'foo');
|
||||
assert.equal(
|
||||
JSON.parse(sjcl.decrypt(encryptionKey, poll.description)),
|
||||
'bar'
|
||||
);
|
||||
});
|
||||
|
||||
test('user factory | encrypts properties', function(assert) {
|
||||
test('user factory | encrypts properties', function (assert) {
|
||||
let encryptionKey = 'abc';
|
||||
let user = this.server.create('user', {
|
||||
encryptionKey,
|
||||
name: 'foo'
|
||||
name: 'foo',
|
||||
});
|
||||
assert.equal(JSON.parse(sjcl.decrypt(encryptionKey, get(user, 'name'))), 'foo');
|
||||
assert.equal(JSON.parse(sjcl.decrypt(encryptionKey, user.name)), 'foo');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,23 +3,23 @@ import { setupRenderingTest } from 'ember-qunit';
|
|||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Modifier | autofocus', function(hooks) {
|
||||
module('Integration | Modifier | autofocus', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it focuses the element', async function(assert) {
|
||||
test('it focuses the element', async function (assert) {
|
||||
await render(hbs`<input {{autofocus}} />`);
|
||||
|
||||
assert.dom('input').isFocused();
|
||||
});
|
||||
|
||||
test('it focuses the element if `enabled` is `true`', async function(assert) {
|
||||
test('it focuses the element if `enabled` is `true`', async function (assert) {
|
||||
this.set('enabled', true);
|
||||
await render(hbs`<input {{autofocus enabled=this.enabled}} />`);
|
||||
|
||||
assert.dom('input').isFocused();
|
||||
});
|
||||
|
||||
test('it does not focus the element if `enabled` is `false`', async function(assert) {
|
||||
test('it does not focus the element if `enabled` is `false`', async function (assert) {
|
||||
this.set('enabled', false);
|
||||
await render(hbs`<input {{autofocus enabled=this.enabled}} />`);
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ import { module, test } from 'qunit';
|
|||
import { setupTest } from 'ember-qunit';
|
||||
import localesMeta from 'croodle/locales/meta';
|
||||
|
||||
module('Integration | translations', function(hooks) {
|
||||
module('Integration | translations', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('all locales have an entry in locales/meta', function(assert) {
|
||||
test('all locales have an entry in locales/meta', function (assert) {
|
||||
let intl = this.owner.lookup('service:intl');
|
||||
|
||||
intl.locales.forEach((locale) => {
|
||||
assert.ok(Object.keys(localesMeta).includes(locale), `locales meta data is present for ${locale}`);
|
||||
assert.ok(
|
||||
Object.keys(localesMeta).includes(locale),
|
||||
`locales meta data is present for ${locale}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,17 +2,14 @@ import PageObject from 'ember-cli-page-object';
|
|||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
||||
const {
|
||||
assign
|
||||
} = Object;
|
||||
const { assign } = Object;
|
||||
|
||||
const {
|
||||
fillable,
|
||||
visitable
|
||||
} = PageObject;
|
||||
const { fillable, visitable } = PageObject;
|
||||
|
||||
export default PageObject.create(assign({}, defaultsForCreate, {
|
||||
pollType: fillable('.poll-type select'),
|
||||
pollTypeHasFocus: hasFocus('.poll-type select'),
|
||||
visit: visitable('/create')
|
||||
}));
|
||||
export default PageObject.create(
|
||||
assign({}, defaultsForCreate, {
|
||||
pollType: fillable('.poll-type select'),
|
||||
pollTypeHasFocus: hasFocus('.poll-type select'),
|
||||
visit: visitable('/create'),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -2,16 +2,14 @@ import PageObject from 'ember-cli-page-object';
|
|||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
||||
const {
|
||||
assign
|
||||
} = Object;
|
||||
const { assign } = Object;
|
||||
|
||||
let {
|
||||
fillable
|
||||
} = PageObject;
|
||||
let { fillable } = PageObject;
|
||||
|
||||
export default PageObject.create(assign({}, defaultsForCreate, {
|
||||
description: fillable('.description textarea'),
|
||||
title: fillable('.title input'),
|
||||
titleHasFocus: hasFocus('.title input')
|
||||
}));
|
||||
export default PageObject.create(
|
||||
assign({}, defaultsForCreate, {
|
||||
description: fillable('.description textarea'),
|
||||
title: fillable('.title input'),
|
||||
titleHasFocus: hasFocus('.title input'),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -2,36 +2,30 @@ import PageObject from 'ember-cli-page-object';
|
|||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
||||
const {
|
||||
assign
|
||||
} = Object;
|
||||
const { assign } = Object;
|
||||
|
||||
let {
|
||||
clickable,
|
||||
collection,
|
||||
fillable,
|
||||
hasClass,
|
||||
text
|
||||
} = PageObject;
|
||||
let { clickable, collection, fillable, hasClass, text } = PageObject;
|
||||
|
||||
export default PageObject.create(assign({}, defaultsForCreate, {
|
||||
days: collection({
|
||||
itemScope: '.form-group',
|
||||
labels: text('label:not(.sr-only)', { multiple: true })
|
||||
}),
|
||||
times: collection({
|
||||
itemScope: '.form-group',
|
||||
item: {
|
||||
add: clickable('button.add'),
|
||||
delete: clickable('button.delete'),
|
||||
label: text('label'),
|
||||
labelIsHidden: hasClass('label', 'sr-only'),
|
||||
time: fillable('input')
|
||||
}
|
||||
}),
|
||||
firstTime: {
|
||||
scope: '.form-group:first',
|
||||
export default PageObject.create(
|
||||
assign({}, defaultsForCreate, {
|
||||
days: collection({
|
||||
itemScope: '.form-group',
|
||||
labels: text('label:not(.sr-only)', { multiple: true }),
|
||||
}),
|
||||
times: collection({
|
||||
itemScope: '.form-group',
|
||||
item: {
|
||||
add: clickable('button.add'),
|
||||
delete: clickable('button.delete'),
|
||||
label: text('label'),
|
||||
labelIsHidden: hasClass('label', 'sr-only'),
|
||||
time: fillable('input'),
|
||||
},
|
||||
}),
|
||||
firstTime: {
|
||||
scope: '.form-group:first',
|
||||
|
||||
inputHasFocus: hasFocus('input')
|
||||
}
|
||||
}));
|
||||
inputHasFocus: hasFocus('input'),
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
fillable,
|
||||
hasClass,
|
||||
isVisible,
|
||||
text
|
||||
text,
|
||||
} from 'ember-cli-page-object';
|
||||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
@ -21,35 +21,44 @@ function selectDates(selector) {
|
|||
async value(dateOrDateTimes) {
|
||||
assert(
|
||||
'selectDates expects an array of date or DateTime (luxon) objects as frist argument',
|
||||
isArray(dateOrDateTimes) && dateOrDateTimes.every((dateOrDateTime) => dateOrDateTime instanceof Date || DateTime.isDateTime(dateOrDateTime))
|
||||
)
|
||||
isArray(dateOrDateTimes) &&
|
||||
dateOrDateTimes.every(
|
||||
(dateOrDateTime) =>
|
||||
dateOrDateTime instanceof Date ||
|
||||
DateTime.isDateTime(dateOrDateTime)
|
||||
)
|
||||
);
|
||||
|
||||
for (let i = 0; i < dateOrDateTimes.length; i++) {
|
||||
let dateOrDateTime = dateOrDateTimes[i];
|
||||
let date = DateTime.isDateTime(dateOrDateTime) ? dateOrDateTime.toJSDate() : dateOrDateTime;
|
||||
let date = DateTime.isDateTime(dateOrDateTime)
|
||||
? dateOrDateTime.toJSDate()
|
||||
: dateOrDateTime;
|
||||
await calendarSelect(selector, date);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default create(assign({}, defaultsForCreate, {
|
||||
selectDates: selectDates('[data-test-form-element-for="days"]'),
|
||||
dateHasError: isVisible('.days.has-error'),
|
||||
dateError: text('.days .help-block'),
|
||||
export default create(
|
||||
assign({}, defaultsForCreate, {
|
||||
selectDates: selectDates('[data-test-form-element-for="days"]'),
|
||||
dateHasError: isVisible('.days.has-error'),
|
||||
dateError: text('.days .help-block'),
|
||||
|
||||
textOptions: collection({
|
||||
itemScope: '.form-group.option',
|
||||
item: {
|
||||
add: clickable('button.add'),
|
||||
delete: clickable('button.delete'),
|
||||
hasError: hasClass('is-invalid', 'input'),
|
||||
title: fillable('input')
|
||||
}
|
||||
}),
|
||||
firstTextOption: {
|
||||
scope: '.form-group.option:first',
|
||||
textOptions: collection({
|
||||
itemScope: '.form-group.option',
|
||||
item: {
|
||||
add: clickable('button.add'),
|
||||
delete: clickable('button.delete'),
|
||||
hasError: hasClass('is-invalid', 'input'),
|
||||
title: fillable('input'),
|
||||
},
|
||||
}),
|
||||
firstTextOption: {
|
||||
scope: '.form-group.option:first',
|
||||
|
||||
inputHasFocus: hasFocus('input')
|
||||
}
|
||||
}));
|
||||
inputHasFocus: hasFocus('input'),
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -2,18 +2,15 @@ import PageObject from 'ember-cli-page-object';
|
|||
import { defaultsForCreate } from 'croodle/tests/pages/defaults';
|
||||
import { hasFocus } from 'croodle/tests/pages/helpers';
|
||||
|
||||
const {
|
||||
assign
|
||||
} = Object;
|
||||
const { assign } = Object;
|
||||
|
||||
const {
|
||||
fillable,
|
||||
visitable
|
||||
} = PageObject;
|
||||
const { fillable, visitable } = PageObject;
|
||||
|
||||
export default PageObject.create(assign({}, defaultsForCreate, {
|
||||
availableAnswers: fillable('.answer-type select'),
|
||||
availableAnswersHasFocus: hasFocus('.answer-type select'),
|
||||
save: defaultsForCreate.next,
|
||||
visit: visitable('/create/settings')
|
||||
}));
|
||||
export default PageObject.create(
|
||||
assign({}, defaultsForCreate, {
|
||||
availableAnswers: fillable('.answer-type select'),
|
||||
availableAnswersHasFocus: hasFocus('.answer-type select'),
|
||||
save: defaultsForCreate.next,
|
||||
visit: visitable('/create/settings'),
|
||||
})
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue