parent
4c1c312c65
commit
3ff2ba463a
|
@ -0,0 +1,39 @@
|
|||
import TutorialMachineConfig from 'vault/machines/tutorial-machine';
|
||||
import SecretsMachineConfig from 'vault/machines/secrets-machine';
|
||||
import PoliciesMachineConfig from 'vault/machines/policies-machine';
|
||||
import ReplicationMachineConfig from 'vault/machines/replication-machine';
|
||||
import ToolsMachineConfig from 'vault/machines/tools-machine';
|
||||
import AuthMachineConfig from 'vault/machines/auth-machine';
|
||||
|
||||
export const STORAGE_KEYS = {
|
||||
TUTORIAL_STATE: 'vault:ui-tutorial-state',
|
||||
FEATURE_LIST: 'vault:ui-feature-list',
|
||||
FEATURE_STATE: 'vault:ui-feature-state',
|
||||
COMPLETED_FEATURES: 'vault:ui-completed-list',
|
||||
COMPONENT_STATE: 'vault:ui-component-state',
|
||||
RESUME_URL: 'vault:ui-tutorial-resume-url',
|
||||
RESUME_ROUTE: 'vault:ui-tutorial-resume-route',
|
||||
};
|
||||
|
||||
export const MACHINES = {
|
||||
tutorial: TutorialMachineConfig,
|
||||
secrets: SecretsMachineConfig,
|
||||
policies: PoliciesMachineConfig,
|
||||
replication: ReplicationMachineConfig,
|
||||
tools: ToolsMachineConfig,
|
||||
authentication: AuthMachineConfig,
|
||||
};
|
||||
|
||||
export const DEFAULTS = {
|
||||
currentState: null,
|
||||
featureList: null,
|
||||
featureState: null,
|
||||
currentMachine: null,
|
||||
tutorialComponent: null,
|
||||
featureComponent: null,
|
||||
stepComponent: null,
|
||||
detailsComponent: null,
|
||||
componentState: null,
|
||||
nextFeature: null,
|
||||
nextStep: null,
|
||||
};
|
|
@ -19,7 +19,6 @@ export default {
|
|||
},
|
||||
complete: {
|
||||
onEntry: ['completeFeature'],
|
||||
on: { RESET: 'idle' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -43,7 +43,7 @@ export default {
|
|||
cond: type => ['pki', 'aws', 'ssh'].includes(type),
|
||||
},
|
||||
secret: {
|
||||
cond: type => ['cubbyhole', 'database', 'gcp', 'kv', 'nomad', 'rabbitmq', 'totp'].includes(type),
|
||||
cond: type => ['kv'].includes(type),
|
||||
},
|
||||
encryption: {
|
||||
cond: type => type === 'transit',
|
||||
|
@ -108,7 +108,7 @@ export default {
|
|||
actions: [{ type: 'routeTransition', params: ['vault.cluster.secrets.backend.create-root'] }],
|
||||
},
|
||||
secret: {
|
||||
cond: type => ['cubbyhole', 'database', 'gcp', 'kv', 'nomad', 'rabbitmq', 'totp'].includes(type),
|
||||
cond: type => ['kv'].includes(type),
|
||||
actions: [{ type: 'routeTransition', params: ['vault.cluster.secrets.backend.create-root'] }],
|
||||
},
|
||||
encryption: {
|
||||
|
|
|
@ -55,7 +55,6 @@ export default {
|
|||
},
|
||||
complete: {
|
||||
onEntry: ['completeFeature'],
|
||||
on: { RESET: 'idle' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -4,44 +4,9 @@ import Service, { inject as service } from '@ember/service';
|
|||
import { Machine } from 'xstate';
|
||||
|
||||
import getStorage from 'vault/lib/token-storage';
|
||||
|
||||
import TutorialMachineConfig from 'vault/machines/tutorial-machine';
|
||||
import SecretsMachineConfig from 'vault/machines/secrets-machine';
|
||||
import PoliciesMachineConfig from 'vault/machines/policies-machine';
|
||||
import ReplicationMachineConfig from 'vault/machines/replication-machine';
|
||||
import ToolsMachineConfig from 'vault/machines/tools-machine';
|
||||
import AuthMachineConfig from 'vault/machines/auth-machine';
|
||||
|
||||
const TutorialMachine = Machine(TutorialMachineConfig);
|
||||
import { STORAGE_KEYS, DEFAULTS, MACHINES } from 'vault/helpers/wizard-constants';
|
||||
const TutorialMachine = Machine(MACHINES.tutorial);
|
||||
let FeatureMachine = null;
|
||||
const TUTORIAL_STATE = 'vault:ui-tutorial-state';
|
||||
const FEATURE_LIST = 'vault:ui-feature-list';
|
||||
const FEATURE_STATE = 'vault:ui-feature-state';
|
||||
const COMPLETED_FEATURES = 'vault:ui-completed-list';
|
||||
const COMPONENT_STATE = 'vault:ui-component-state';
|
||||
const RESUME_URL = 'vault:ui-tutorial-resume-url';
|
||||
const RESUME_ROUTE = 'vault:ui-tutorial-resume-route';
|
||||
const MACHINES = {
|
||||
secrets: SecretsMachineConfig,
|
||||
policies: PoliciesMachineConfig,
|
||||
replication: ReplicationMachineConfig,
|
||||
tools: ToolsMachineConfig,
|
||||
authentication: AuthMachineConfig,
|
||||
};
|
||||
|
||||
const DEFAULTS = {
|
||||
currentState: null,
|
||||
featureList: null,
|
||||
featureState: null,
|
||||
currentMachine: null,
|
||||
tutorialComponent: null,
|
||||
featureComponent: null,
|
||||
stepComponent: null,
|
||||
detailsComponent: null,
|
||||
componentState: null,
|
||||
nextFeature: null,
|
||||
nextStep: null,
|
||||
};
|
||||
|
||||
export default Service.extend(DEFAULTS, {
|
||||
router: service(),
|
||||
|
@ -53,25 +18,25 @@ export default Service.extend(DEFAULTS, {
|
|||
},
|
||||
|
||||
initializeMachines() {
|
||||
if (!this.storageHasKey(TUTORIAL_STATE)) {
|
||||
if (!this.storageHasKey(STORAGE_KEYS.TUTORIAL_STATE)) {
|
||||
let state = TutorialMachine.initialState;
|
||||
this.saveState('currentState', state.value);
|
||||
this.saveExtState(TUTORIAL_STATE, state.value);
|
||||
this.saveExtState(STORAGE_KEYS.TUTORIAL_STATE, state.value);
|
||||
}
|
||||
this.saveState('currentState', this.getExtState(TUTORIAL_STATE));
|
||||
if (this.storageHasKey(COMPONENT_STATE)) {
|
||||
this.set('componentState', this.getExtState(COMPONENT_STATE));
|
||||
this.saveState('currentState', this.getExtState(STORAGE_KEYS.TUTORIAL_STATE));
|
||||
if (this.storageHasKey(STORAGE_KEYS.COMPONENT_STATE)) {
|
||||
this.set('componentState', this.getExtState(STORAGE_KEYS.COMPONENT_STATE));
|
||||
}
|
||||
let stateNodes = TutorialMachine.getStateNodes(this.get('currentState'));
|
||||
this.executeActions(stateNodes.reduce((acc, node) => acc.concat(node.onEntry), []), null, 'tutorial');
|
||||
if (this.storageHasKey(FEATURE_LIST)) {
|
||||
this.set('featureList', this.getExtState(FEATURE_LIST));
|
||||
if (this.storageHasKey(FEATURE_STATE)) {
|
||||
this.saveState('featureState', this.getExtState(FEATURE_STATE));
|
||||
if (this.storageHasKey(STORAGE_KEYS.FEATURE_LIST)) {
|
||||
this.set('featureList', this.getExtState(STORAGE_KEYS.FEATURE_LIST));
|
||||
if (this.storageHasKey(STORAGE_KEYS.FEATURE_STATE)) {
|
||||
this.saveState('featureState', this.getExtState(STORAGE_KEYS.FEATURE_STATE));
|
||||
} else {
|
||||
if (FeatureMachine != null) {
|
||||
this.saveState('featureState', FeatureMachine.initialState);
|
||||
this.saveExtState(FEATURE_STATE, this.get('featureState'));
|
||||
this.saveExtState(STORAGE_KEYS.FEATURE_STATE, this.get('featureState'));
|
||||
}
|
||||
}
|
||||
this.buildFeatureMachine();
|
||||
|
@ -82,13 +47,13 @@ export default Service.extend(DEFAULTS, {
|
|||
let storage = this.storage();
|
||||
// empty storage
|
||||
[
|
||||
TUTORIAL_STATE,
|
||||
FEATURE_LIST,
|
||||
FEATURE_STATE,
|
||||
COMPLETED_FEATURES,
|
||||
COMPONENT_STATE,
|
||||
RESUME_URL,
|
||||
RESUME_ROUTE,
|
||||
STORAGE_KEYS.TUTORIAL_STATE,
|
||||
STORAGE_KEYS.FEATURE_LIST,
|
||||
STORAGE_KEYS.FEATURE_STATE,
|
||||
STORAGE_KEYS.COMPLETED_FEATURES,
|
||||
STORAGE_KEYS.COMPONENT_STATE,
|
||||
STORAGE_KEYS.RESUME_URL,
|
||||
STORAGE_KEYS.RESUME_ROUTE,
|
||||
].forEach(key => storage.removeItem(key));
|
||||
// reset wizard state
|
||||
this.setProperties(DEFAULTS);
|
||||
|
@ -115,11 +80,11 @@ export default Service.extend(DEFAULTS, {
|
|||
transitionTutorialMachine(currentState, event, extendedState) {
|
||||
if (extendedState) {
|
||||
this.set('componentState', extendedState);
|
||||
this.saveExtState(COMPONENT_STATE, extendedState);
|
||||
this.saveExtState(STORAGE_KEYS.COMPONENT_STATE, extendedState);
|
||||
}
|
||||
let { actions, value } = TutorialMachine.transition(currentState, event);
|
||||
this.saveState('currentState', value);
|
||||
this.saveExtState(TUTORIAL_STATE, this.get('currentState'));
|
||||
this.saveExtState(STORAGE_KEYS.TUTORIAL_STATE, this.get('currentState'));
|
||||
this.executeActions(actions, event, 'tutorial');
|
||||
},
|
||||
|
||||
|
@ -129,12 +94,12 @@ export default Service.extend(DEFAULTS, {
|
|||
}
|
||||
if (extendedState) {
|
||||
this.set('componentState', extendedState);
|
||||
this.saveExtState(COMPONENT_STATE, extendedState);
|
||||
this.saveExtState(STORAGE_KEYS.COMPONENT_STATE, extendedState);
|
||||
}
|
||||
|
||||
let { actions, value } = FeatureMachine.transition(currentState, event, this.get('componentState'));
|
||||
this.saveState('featureState', value);
|
||||
this.saveExtState(FEATURE_STATE, value);
|
||||
this.saveExtState(STORAGE_KEYS.FEATURE_STATE, value);
|
||||
this.executeActions(actions, event, 'feature');
|
||||
// if all features were completed, the FeatureMachine gets nulled
|
||||
// out and won't exist here as there is no next step
|
||||
|
@ -229,33 +194,33 @@ export default Service.extend(DEFAULTS, {
|
|||
handlePaused() {
|
||||
let expected = this.get('expectedURL');
|
||||
if (expected) {
|
||||
this.saveExtState(RESUME_URL, this.get('expectedURL'));
|
||||
this.saveExtState(RESUME_ROUTE, this.get('expectedRouteName'));
|
||||
this.saveExtState(STORAGE_KEYS.RESUME_URL, this.get('expectedURL'));
|
||||
this.saveExtState(STORAGE_KEYS.RESUME_ROUTE, this.get('expectedRouteName'));
|
||||
}
|
||||
},
|
||||
|
||||
handleResume() {
|
||||
let resumeURL = this.storage().getItem(RESUME_URL);
|
||||
let resumeURL = this.storage().getItem(STORAGE_KEYS.RESUME_URL);
|
||||
if (!resumeURL) {
|
||||
return;
|
||||
}
|
||||
this.get('router').transitionTo(resumeURL).followRedirects().then(() => {
|
||||
this.set('expectedRouteName', this.storage().getItem(RESUME_ROUTE));
|
||||
this.set('expectedRouteName', this.storage().getItem(STORAGE_KEYS.RESUME_ROUTE));
|
||||
this.set('expectedURL', resumeURL);
|
||||
this.initializeMachines();
|
||||
this.storage().removeItem(RESUME_URL);
|
||||
this.storage().removeItem(STORAGE_KEYS.RESUME_URL);
|
||||
});
|
||||
},
|
||||
|
||||
handleDismissed() {
|
||||
this.storage().removeItem(FEATURE_STATE);
|
||||
this.storage().removeItem(FEATURE_LIST);
|
||||
this.storage().removeItem(COMPONENT_STATE);
|
||||
this.storage().removeItem(STORAGE_KEYS.FEATURE_STATE);
|
||||
this.storage().removeItem(STORAGE_KEYS.FEATURE_LIST);
|
||||
this.storage().removeItem(STORAGE_KEYS.COMPONENT_STATE);
|
||||
},
|
||||
|
||||
saveFeatures(features) {
|
||||
this.set('featureList', features);
|
||||
this.saveExtState(FEATURE_LIST, this.get('featureList'));
|
||||
this.saveExtState(STORAGE_KEYS.FEATURE_LIST, this.get('featureList'));
|
||||
this.buildFeatureMachine();
|
||||
},
|
||||
|
||||
|
@ -264,10 +229,10 @@ export default Service.extend(DEFAULTS, {
|
|||
return;
|
||||
}
|
||||
this.startFeature();
|
||||
if (this.storageHasKey(FEATURE_STATE)) {
|
||||
this.saveState('featureState', this.getExtState(FEATURE_STATE));
|
||||
if (this.storageHasKey(STORAGE_KEYS.FEATURE_STATE)) {
|
||||
this.saveState('featureState', this.getExtState(STORAGE_KEYS.FEATURE_STATE));
|
||||
}
|
||||
this.saveExtState(FEATURE_STATE, this.get('featureState'));
|
||||
this.saveExtState(STORAGE_KEYS.FEATURE_STATE, this.get('featureState'));
|
||||
let nextFeature =
|
||||
this.get('featureList').length > 1 ? this.get('featureList').objectAt(1).capitalize() : 'Finish';
|
||||
this.set('nextFeature', nextFeature);
|
||||
|
@ -292,20 +257,23 @@ export default Service.extend(DEFAULTS, {
|
|||
completeFeature() {
|
||||
let features = this.get('featureList');
|
||||
let done = features.shift();
|
||||
if (!this.getExtState(COMPLETED_FEATURES)) {
|
||||
if (!this.getExtState(STORAGE_KEYS.COMPLETED_FEATURES)) {
|
||||
let completed = [];
|
||||
completed.push(done);
|
||||
this.saveExtState(COMPLETED_FEATURES, completed);
|
||||
this.saveExtState(STORAGE_KEYS.COMPLETED_FEATURES, completed);
|
||||
} else {
|
||||
this.saveExtState(COMPLETED_FEATURES, this.getExtState(COMPLETED_FEATURES).toArray().addObject(done));
|
||||
this.saveExtState(
|
||||
STORAGE_KEYS.COMPLETED_FEATURES,
|
||||
this.getExtState(STORAGE_KEYS.COMPLETED_FEATURES).toArray().addObject(done)
|
||||
);
|
||||
}
|
||||
|
||||
this.saveExtState(FEATURE_LIST, features.length ? features : null);
|
||||
this.storage().removeItem(FEATURE_STATE);
|
||||
this.saveExtState(STORAGE_KEYS.FEATURE_LIST, features.length ? features : null);
|
||||
this.storage().removeItem(STORAGE_KEYS.FEATURE_STATE);
|
||||
if (features.length > 0) {
|
||||
this.buildFeatureMachine();
|
||||
} else {
|
||||
this.storage().removeItem(FEATURE_LIST);
|
||||
this.storage().removeItem(STORAGE_KEYS.FEATURE_LIST);
|
||||
FeatureMachine = null;
|
||||
this.transitionTutorialMachine(this.get('currentState'), 'DONE');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { Machine } from 'xstate';
|
||||
import AuthMachineConfig from 'vault/machines/auth-machine';
|
||||
|
||||
module('Unit | Machine | auth-machine', function() {
|
||||
const authMachine = Machine(AuthMachineConfig);
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
currentState: authMachine.initialState,
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'enable',
|
||||
actions: [
|
||||
{ component: 'wizard/mounts-wizard', level: 'feature', type: 'render' },
|
||||
{ component: 'wizard/auth-enable', level: 'step', type: 'render' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'enable',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'list',
|
||||
actions: [
|
||||
{ component: 'wizard/auth-list', level: 'step', type: 'render' },
|
||||
{ component: 'wizard/mounts-wizard', level: 'feature', type: 'render' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'list',
|
||||
event: 'DETAILS',
|
||||
expectedResults: {
|
||||
value: 'details',
|
||||
actions: [
|
||||
{ component: 'wizard/auth-details', level: 'step', type: 'render' },
|
||||
{ component: 'wizard/mounts-wizard', level: 'feature', type: 'render' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'details',
|
||||
event: 'CONTINUE',
|
||||
expectedResults: {
|
||||
value: 'complete',
|
||||
actions: ['completeFeature'],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'details',
|
||||
event: 'RESET',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'idle',
|
||||
actions: [
|
||||
{
|
||||
params: ['vault.cluster.settings.auth.enable'],
|
||||
type: 'routeTransition',
|
||||
},
|
||||
{
|
||||
component: 'wizard/mounts-wizard',
|
||||
level: 'feature',
|
||||
type: 'render',
|
||||
},
|
||||
{
|
||||
component: 'wizard/auth-idle',
|
||||
level: 'step',
|
||||
type: 'render',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
test(`transition: ${testCase.event} for currentState ${testCase.currentState} and componentState ${
|
||||
testCase.params
|
||||
}`, function(assert) {
|
||||
let result = authMachine.transition(testCase.currentState, testCase.event, testCase.params);
|
||||
assert.equal(result.value, testCase.expectedResults.value);
|
||||
assert.deepEqual(result.actions, testCase.expectedResults.actions);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { Machine } from 'xstate';
|
||||
import PoliciesMachineConfig from 'vault/machines/policies-machine';
|
||||
|
||||
module('Unit | Machine | policies-machine', function() {
|
||||
const policiesMachine = Machine(PoliciesMachineConfig);
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
currentState: policiesMachine.initialState,
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'create',
|
||||
actions: [{ component: 'wizard/policies-create', level: 'feature', type: 'render' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'create',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'details',
|
||||
actions: [{ component: 'wizard/policies-details', level: 'feature', type: 'render' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'details',
|
||||
event: 'CONTINUE',
|
||||
expectedResults: {
|
||||
value: 'delete',
|
||||
actions: [{ component: 'wizard/policies-delete', level: 'feature', type: 'render' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'delete',
|
||||
event: 'CONTINUE',
|
||||
expectedResults: {
|
||||
value: 'others',
|
||||
actions: [{ component: 'wizard/policies-others', level: 'feature', type: 'render' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'others',
|
||||
event: 'CONTINUE',
|
||||
expectedResults: {
|
||||
value: 'complete',
|
||||
actions: ['completeFeature'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
test(`transition: ${testCase.event} for currentState ${testCase.currentState} and componentState ${
|
||||
testCase.params
|
||||
}`, function(assert) {
|
||||
let result = policiesMachine.transition(testCase.currentState, testCase.event, testCase.params);
|
||||
assert.equal(result.value, testCase.expectedResults.value);
|
||||
assert.deepEqual(result.actions, testCase.expectedResults.actions);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { Machine } from 'xstate';
|
||||
import ReplicationMachineConfig from 'vault/machines/replication-machine';
|
||||
|
||||
module('Unit | Machine | replication-machine', function() {
|
||||
const replicationMachine = Machine(ReplicationMachineConfig);
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
currentState: replicationMachine.initialState,
|
||||
event: 'ENABLEREPLICATION',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'details',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/replication-details' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'details',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'complete',
|
||||
actions: ['completeFeature'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
test(`transition: ${testCase.event} for currentState ${testCase.currentState} and componentState ${
|
||||
testCase.params
|
||||
}`, function(assert) {
|
||||
let result = replicationMachine.transition(testCase.currentState, testCase.event, testCase.params);
|
||||
assert.equal(result.value, testCase.expectedResults.value);
|
||||
assert.deepEqual(result.actions, testCase.expectedResults.actions);
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,91 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { Machine } from 'xstate';
|
||||
import ToolsMachineConfig from 'vault/machines/tools-machine';
|
||||
|
||||
module('Unit | Machine | tools-machine', function() {
|
||||
const toolsMachine = Machine(ToolsMachineConfig);
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
currentState: toolsMachine.initialState,
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'wrapped',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-wrapped' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'wrapped',
|
||||
event: 'LOOKUP',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'lookup',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-lookup' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'lookup',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'info',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-info' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'info',
|
||||
event: 'REWRAP',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'rewrap',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-rewrap' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'rewrap',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'rewrapped',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-rewrapped' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'rewrapped',
|
||||
event: 'UNWRAP',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'unwrap',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-unwrap' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'unwrap',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'unwrapped',
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/tools-unwrapped' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'unwrapped',
|
||||
event: 'CONTINUE',
|
||||
expectedResults: {
|
||||
value: 'complete',
|
||||
actions: ['completeFeature'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
test(`transition: ${testCase.event} for currentState ${testCase.currentState} and componentState ${
|
||||
testCase.params
|
||||
}`, function(assert) {
|
||||
let result = toolsMachine.transition(testCase.currentState, testCase.event, testCase.params);
|
||||
assert.equal(result.value, testCase.expectedResults.value);
|
||||
assert.deepEqual(result.actions, testCase.expectedResults.actions);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,247 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { Machine } from 'xstate';
|
||||
import TutorialMachineConfig from 'vault/machines/tutorial-machine';
|
||||
|
||||
module('Unit | Machine | tutorial-machine', function() {
|
||||
const tutorialMachine = Machine(TutorialMachineConfig);
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
currentState: 'init',
|
||||
event: 'START',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'setup',
|
||||
},
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/init-setup' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init.active.setup',
|
||||
event: 'TOSAVE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'save',
|
||||
},
|
||||
},
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/init-save-keys' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init',
|
||||
event: 'SAVE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'save',
|
||||
},
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/init-save-keys' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init.active.save',
|
||||
event: 'TOUNSEAL',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'unseal',
|
||||
},
|
||||
},
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/init-unseal' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init',
|
||||
event: 'UNSEAL',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'unseal',
|
||||
},
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/init-unseal' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init.active.unseal',
|
||||
event: 'TOLOGIN',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'login',
|
||||
},
|
||||
},
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/init-login' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init',
|
||||
event: 'LOGIN',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: {
|
||||
active: 'login',
|
||||
},
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/init-login' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'init.active.login',
|
||||
event: 'INITDONE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
active: 'select',
|
||||
},
|
||||
actions: [
|
||||
'showTutorialWhenAuthenticated',
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/features-selection' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'active.select',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
active: 'feature',
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'active.feature',
|
||||
event: 'DISMISS',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'dismissed',
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: null },
|
||||
{ type: 'render', level: 'feature', component: null },
|
||||
{ type: 'render', level: 'step', component: null },
|
||||
{ type: 'render', level: 'detail', component: null },
|
||||
'handleDismissed',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'active.feature',
|
||||
event: 'DONE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'complete',
|
||||
actions: [
|
||||
{ type: 'render', level: 'feature', component: null },
|
||||
{ type: 'render', level: 'step', component: null },
|
||||
{ type: 'render', level: 'detail', component: null },
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-complete' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'active.feature',
|
||||
event: 'PAUSE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: 'paused',
|
||||
actions: [
|
||||
{ type: 'render', level: 'feature', component: null },
|
||||
{ type: 'render', level: 'step', component: null },
|
||||
{ type: 'render', level: 'detail', component: null },
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-paused' },
|
||||
'handlePaused',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'paused',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
active: 'feature',
|
||||
},
|
||||
actions: ['handleResume', { type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'idle',
|
||||
event: 'INIT',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
init: 'idle',
|
||||
},
|
||||
actions: [
|
||||
'showTutorialAlways',
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-idle' },
|
||||
{ type: 'render', level: 'feature', component: null },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'idle',
|
||||
event: 'AUTH',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
active: 'select',
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/features-selection' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
currentState: 'idle',
|
||||
event: 'CONTINUE',
|
||||
params: null,
|
||||
expectedResults: {
|
||||
value: {
|
||||
active: 'select',
|
||||
},
|
||||
actions: [
|
||||
{ type: 'render', level: 'tutorial', component: 'wizard/tutorial-active' },
|
||||
{ type: 'render', level: 'feature', component: 'wizard/features-selection' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
test(`transition: ${testCase.event} for currentState ${testCase.currentState} and componentState ${
|
||||
testCase.params
|
||||
}`, function(assert) {
|
||||
let result = tutorialMachine.transition(testCase.currentState, testCase.event, testCase.params);
|
||||
assert.deepEqual(result.value, testCase.expectedResults.value);
|
||||
assert.deepEqual(result.actions, testCase.expectedResults.actions);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,258 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import Service from '@ember/service';
|
||||
import sinon from 'sinon';
|
||||
import { STORAGE_KEYS, DEFAULTS } from 'vault/helpers/wizard-constants';
|
||||
|
||||
let routerStub = Service.extend({
|
||||
transitionTo: sinon.stub().returns({
|
||||
followRedirects: function() {
|
||||
return {
|
||||
then: function(callback) {
|
||||
callback();
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
urlFor: sinon.stub().returns('/ui/vault/foo'),
|
||||
});
|
||||
|
||||
module('Unit | Service | wizard', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.owner.register('service:router', routerStub);
|
||||
this.router = this.owner.lookup('service:router');
|
||||
});
|
||||
|
||||
function storage() {
|
||||
return {
|
||||
items: {},
|
||||
getItem(key) {
|
||||
var item = this.items[key];
|
||||
return item && JSON.parse(item);
|
||||
},
|
||||
|
||||
setItem(key, val) {
|
||||
return (this.items[key] = JSON.stringify(val));
|
||||
},
|
||||
|
||||
removeItem(key) {
|
||||
delete this.items[key];
|
||||
},
|
||||
|
||||
keys() {
|
||||
return Object.keys(this.items);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let testCases = [
|
||||
{
|
||||
method: 'getExtState',
|
||||
args: [STORAGE_KEYS.TUTORIAL_STATE],
|
||||
expectedResults: {
|
||||
storage: [{ key: STORAGE_KEYS.TUTORIAL_STATE, value: 'idle' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'saveExtState',
|
||||
args: [STORAGE_KEYS.TUTORIAL_STATE, 'test'],
|
||||
expectedResults: {
|
||||
storage: [{ key: STORAGE_KEYS.TUTORIAL_STATE, value: 'test' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'storageHasKey',
|
||||
args: ['fake-key'],
|
||||
expectedResults: { value: false },
|
||||
},
|
||||
{
|
||||
method: 'storageHasKey',
|
||||
args: [STORAGE_KEYS.TUTORIAL_STATE],
|
||||
expectedResults: { value: true },
|
||||
},
|
||||
{
|
||||
method: 'handleDismissed',
|
||||
args: [],
|
||||
expectedResults: {
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.FEATURE_STATE, value: undefined },
|
||||
{ key: STORAGE_KEYS.FEATURE_LIST, value: undefined },
|
||||
{ key: STORAGE_KEYS.COMPONENT_STATE, value: undefined },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'handlePaused',
|
||||
args: [],
|
||||
properties: {
|
||||
expectedURL: 'this/is/a/url',
|
||||
expectedRouteName: 'this.is.a.route',
|
||||
},
|
||||
expectedResults: {
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: 'this/is/a/url' },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: 'this.is.a.route' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'handlePaused',
|
||||
args: [],
|
||||
expectedResults: {
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: undefined },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: undefined },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'handleResume',
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: 'this/is/a/url' },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: 'this.is.a.route' },
|
||||
],
|
||||
args: [],
|
||||
expectedResults: {
|
||||
props: [
|
||||
{ prop: 'expectedURL', value: 'this/is/a/url' },
|
||||
{ prop: 'expectedRouteName', value: 'this.is.a.route' },
|
||||
],
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: undefined },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: 'this.is.a.route' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'handleResume',
|
||||
args: [],
|
||||
expectedResults: {
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: undefined },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: undefined },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'restartGuide',
|
||||
args: [],
|
||||
expectedResults: {
|
||||
props: [
|
||||
{ prop: 'currentState', value: 'active.select' },
|
||||
{ prop: 'featureComponent', value: 'wizard/features-selection' },
|
||||
{ prop: 'tutorialComponent', value: 'wizard/tutorial-active' },
|
||||
],
|
||||
storage: [
|
||||
{ key: STORAGE_KEYS.FEATURE_STATE, value: undefined },
|
||||
{ key: STORAGE_KEYS.FEATURE_LIST, value: undefined },
|
||||
{ key: STORAGE_KEYS.COMPONENT_STATE, value: undefined },
|
||||
{ key: STORAGE_KEYS.TUTORIAL_STATE, value: 'active.select' },
|
||||
{ key: STORAGE_KEYS.COMPLETED_FEATURES, value: undefined },
|
||||
{ key: STORAGE_KEYS.RESUME_URL, value: undefined },
|
||||
{ key: STORAGE_KEYS.RESUME_ROUTE, value: undefined },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'saveState',
|
||||
args: [
|
||||
'currentState',
|
||||
{
|
||||
value: {
|
||||
init: {
|
||||
active: 'login',
|
||||
},
|
||||
},
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/init-login' }],
|
||||
},
|
||||
],
|
||||
expectedResults: {
|
||||
props: [{ prop: 'currentState', value: 'init.active.login' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'saveState',
|
||||
args: [
|
||||
'currentState',
|
||||
{
|
||||
value: {
|
||||
active: 'login',
|
||||
},
|
||||
actions: [{ type: 'render', level: 'feature', component: 'wizard/init-login' }],
|
||||
},
|
||||
],
|
||||
expectedResults: {
|
||||
props: [{ prop: 'currentState', value: 'active.login' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'saveState',
|
||||
args: ['currentState', 'login'],
|
||||
expectedResults: {
|
||||
props: [{ prop: 'currentState', value: 'login' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'startFeature',
|
||||
args: [],
|
||||
properties: { featureList: ['secrets', 'tools'] },
|
||||
expectedResults: {
|
||||
props: [{ prop: 'featureState', value: 'idle' }, { prop: 'currentMachine', value: 'secrets' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'saveFeatures',
|
||||
args: [['secrets', 'tools']],
|
||||
expectedResults: {
|
||||
props: [{ prop: 'featureList', value: ['secrets', 'tools'] }],
|
||||
storage: [{ key: STORAGE_KEYS.FEATURE_LIST, value: ['secrets', 'tools'] }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(testCase => {
|
||||
let store = storage();
|
||||
test(`${testCase.method}`, function(assert) {
|
||||
let wizard = this.owner.factoryFor('service:wizard').create({
|
||||
storage() {
|
||||
return store;
|
||||
},
|
||||
});
|
||||
|
||||
if (testCase.properties) {
|
||||
wizard.setProperties(testCase.properties);
|
||||
} else {
|
||||
wizard.setProperties(DEFAULTS);
|
||||
}
|
||||
|
||||
if (testCase.storage) {
|
||||
testCase.storage.forEach(item => wizard.storage().setItem(item.key, item.value));
|
||||
}
|
||||
|
||||
let result = wizard[testCase.method](...testCase.args);
|
||||
if (testCase.expectedResults.props) {
|
||||
testCase.expectedResults.props.forEach(property => {
|
||||
assert.deepEqual(
|
||||
wizard.get(property.prop),
|
||||
property.value,
|
||||
`${testCase.method} creates correct value for ${property.prop}`
|
||||
);
|
||||
});
|
||||
}
|
||||
if (testCase.expectedResults.storage) {
|
||||
testCase.expectedResults.storage.forEach(item => {
|
||||
assert.deepEqual(
|
||||
wizard.storage().getItem(item.key),
|
||||
item.value,
|
||||
`${testCase.method} creates correct storage state for ${item.key}`
|
||||
);
|
||||
});
|
||||
}
|
||||
if (testCase.expectedResults.value !== null && testCase.expectedResults.value !== undefined) {
|
||||
assert.equal(result, testCase.expectedResults.value, `${testCase.method} gives correct value`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue