accounts-frontend/packages/app/services/authFlow/AuthFlow.functional.test.ts
2024-12-23 14:53:49 +01:00

162 lines
4.9 KiB
TypeScript

import expect from 'app/test/unexpected';
import sinon from 'sinon';
import { SynchronousPromise } from 'synchronous-promise';
import { Store } from 'redux';
import AuthFlow from 'app/services/authFlow/AuthFlow';
import RegisterState from 'app/services/authFlow/RegisterState';
import ActivationState from 'app/services/authFlow/ActivationState';
import ResendActivationState from 'app/services/authFlow/ResendActivationState';
describe('AuthFlow.functional', () => {
let flow: AuthFlow;
let actions: {};
let store: Store;
let state: { user?: { isGuest: boolean; isActive: boolean } };
let navigate: (path: string, extra?: {}) => void;
beforeEach(() => {
actions = {};
store = {
getState: sinon.stub().named('store.getState'),
// @ts-ignore
dispatch: sinon
.spy(({ type, payload = {} }) => {
if (type === '@@router/TRANSITION' && payload.method === 'push') {
// emulate redux-router
// @ts-ignore
navigate(...payload.args);
}
})
.named('store.dispatch'),
};
state = {};
// @ts-ignore
flow = new AuthFlow(actions);
flow.setStore(store);
let lastUrl: string;
navigate = function navigate(path, extra = {}) {
// emulates router behaviour
if (lastUrl !== path) {
lastUrl = path;
flow.handleRequest({ path, query: new URLSearchParams(), params: {}, ...extra }, navigate);
}
};
sinon.stub(flow, 'run').named('flow.run');
sinon.spy(flow, 'navigate').named('flow.navigate');
// @ts-ignore
store.getState.returns(state);
});
describe('guest', () => {
beforeEach(() => {
Object.assign(state, {
user: {
isGuest: true,
},
auth: {
credentials: {
login: null,
},
},
});
});
it('should redirect guest / -> /login', () => {
navigate('/');
expect(flow.navigate, 'was called twice');
expect(flow.navigate, 'to have a call satisfying', ['/login']);
});
it('should redirect guest to /login after /login -> /', () => {
// this is to ensure, that when AuthFlow is already on LoginState (on /login)
// it will not allow user to go to / (which is forbidden for users) and will
// always redirect to /login, so that enter condition of state is always satisfied
navigate('/login');
navigate('/');
expect(flow.navigate, 'was called thrice');
expect(flow.navigate, 'to have calls satisfying', [['/login'], ['/login'], ['/login']]);
});
});
describe('oauth', () => {
it('should oauth without any rendering if no acceptance required', () => {
const expectedRedirect = 'foo';
Object.assign(state, {
user: {
isGuest: false,
},
auth: {
credentials: {},
oauth: {
params: {
clientId: 123,
},
prompt: [],
},
},
});
// @ts-ignore
flow.run.onCall(0).returns(SynchronousPromise.resolve());
// @ts-ignore
flow.run.onCall(1).returns(
SynchronousPromise.resolve({
redirectUri: expectedRedirect,
}),
);
navigate('/oauth2');
expect(flow.run, 'to have calls satisfying', [
['oAuthValidate', {}],
['oAuthComplete', {}],
['redirect', expectedRedirect],
]);
});
});
describe('/resend-activation #goBack()', () => {
beforeEach(() => {
state.user = {
isGuest: true,
isActive: false,
};
});
it('should goBack to /activation', () => {
navigate('/activation');
expect(flow.state, 'to be a', ActivationState);
flow.state.reject(flow);
expect(flow.state, 'to be a', ResendActivationState);
flow.state.goBack(flow);
expect(flow.state, 'to be a', ActivationState);
});
it('should goBack to /register', () => {
navigate('/register');
expect(flow.state, 'to be a', RegisterState);
flow.state.reject(flow, { requestEmail: true });
expect(flow.state, 'to be a', ResendActivationState);
flow.state.goBack(flow);
expect(flow.state, 'to be a', RegisterState);
});
});
});