⚠️ Draft documentation. May contain typos and inaccuracies.
EN | DE | RU | FR | ES

Commentary: git diff origin/develop -- reducers/authentication.test.js

Date: 2026-05-08

Compared commits:

Reducer (old and new — identical): reducers/authentication.js (40 lines) — did not change between commits (created 2019-02-28).

Note: Links to code in branch draft43npm on fork marked with . Code may not be available on GitHub until the PR merge.

Section 1: Initial state (lines 1–10)

@@ -1,10 +1,10 @@
1 import reducer from './authentication';
2
3 const exampleError = 'Uncool error message.';
4
5 Object.freeze(exampleError);
6+
7+const initialState = {
8+ loading: true,
9+ error: null,
10+ user: null,
11+};
6
7 describe('reducer', () => {
8 it('initial state', () => {
9 expect(reducer(undefined, {}))
10- .toEqual({
11- loading: false,
12- error: null,
13- loggedIn: false,
14- });
+ .toEqual(initialState);

1. initialState extracted as a constant.

Before: object was hardcoded in the test it('initial state') as { loading: false, error: null, loggedIn: false }.

After: the constant initialState, kfromwhich k matches c initialState from the reducer itself [reducers/authentication.js:3].

2. loading: falseloading: true.

Old init: loading: false — user not logged in, no spinner.

New init: loading: true — on app load, loadUserStatus() runs immediately to check the session. Initial state — 'looking for user'. This reducer change commit 8b3b44be7 (2020-03-18).

3. loggedIn: falseuser: null. MAIN STATE SHAPE CHANGE.

{/* Old form */}
{ loading: boolean, error: string|null, loggedIn: boolean }
{/* New form */}
{ loading: boolean, error: string|null, user: object|null }

Section 2: Unknown action (lines 11–21)

@@ -12,19 +12,14 @@
17- it('unknown action', () => {
13+ it('unknown action returns current state', () => {
18 const state = {
19- loading: true,
20- error: null,
21- loggedIn: false,
+ loading: false,
+ error: null,
+ user: { name: 'test' },
22 };
23
24 Object.freeze(state);
25
26- expect(reducer(state, {
27- type: 'UNKNOWN_ACTION',
28- }))
+ expect(reducer(state, { type: 'UNKNOWN_ACTION' }))
29 .toEqual(state);
30 });
31 });

unknown actionunknown action returns current state. Explicit description: 'reducer doesn't know this action → returns current state unchanged'. The old test didn't say what it was actually verifying.

Input state change: old test checked { loading: true, loggedIn: false }, new one — { loading: false, user: { name: 'test' } }. Chose a logged-in user state to verify that user is not nulled on unknown action — guarantee of a pure function [Redux reducers].

Formatting: old call split across 3 lines, new one — single line. Style only.

Object.freeze(state) [MDN] — mutation protection: if the reducer modifies the passed state (violating purity), the test will fail with an error.

Section 3: USER_LOGIN_BEGIN — 2 tests deleted, 1 changed (lines 22–62)

@@ -33,57 +28,40 @@
33-describe('handles USER_LOGIN_BEGIN', () => {
34- it('fresh state', () => {
35- const state = {
36- loading: false,
37- error: null,
38- loggedIn: false,
39- };
40-
41- Object.freeze(state);
42-
43- expect(reducer(state, {
44- type: 'USER_LOGIN_BEGIN',
45- }))
46- .toEqual({
47- loading: true,
48- error: null,
49- loggedIn: false,
50- });
51- });
52-
53- it('error state', () => {
54- const state = {
55- loading: false,
56- error: exampleError,
57- loggedIn: false,
58- };
59-
60- Object.freeze(state);
61-
62- expect(reducer(state, {
63- type: 'USER_LOGIN_BEGIN',
64- }))
65- .toEqual({
66- loading: true,
67- error: null,
68- loggedIn: false,
69- });
70- });
71-
72- it('loggedIn state', () => {
73- const state = {
74- loading: false,
75- error: null,
76- loggedIn: false,
77- };
78-
79- Object.freeze(state);
80-
81- expect(reducer(state, {
82- type: 'USER_LOGIN_BEGIN',
83- }))
84- .toEqual({
85- loading: true,
86- error: null,
87- loggedIn: false,
88- });
89- });
90-});

describe('handles USER_LOGIN_BEGIN')describe('USER_LOGIN_BEGIN'). Removed the word handles — in the new style, all describe blocks are named after the action literally. [new file]

2 of 3 tests deleted.

«fresh state» — verified transition from { loading: false, error: null, ... }{ loading: true, error: null, ... }. [line 34]

«error state» — verified transition from { loading: false, error: exampleError, ... }{ loading: true, error: null, ... }. [line 53]

Both tests verify the same thing: error is nulled, loading becomes true. The difference — in the original error (null vs line). Redundant — one test covers both cases.

«loggedIn state» — input: { loading: false, error: null, loggedIn: false } — completely matches 'fresh state'. Possibly a typo (should have been loggedIn: true). [line 72]

One new test: 'resets to loading with null user' [new file]

Section 4: USER_LOGIN_SUCCESS — 1 test deleted, 1 changed (lines 63–109)

@@ -91,29 +61,18 @@
92-describe('handles USER_LOGIN_SUCCESS', () => {
93- it('loading state', () => {
+describe('USER_LOGIN_SUCCESS', () => {
+ it('sets user and clears loading/error', () => {
94 const state = {
95 loading: true,
96 error: null,
97- loggedIn: false,
+ user: null,
98 };
99
100 Object.freeze(state);
101
102 expect(reducer(state, {
103 type: 'USER_LOGIN_SUCCESS',
+ payload,
106 }))
107
108- .toEqual({
109- loading: false,
110- error: null,
111- loggedIn: true,
112- });
113- });
114-
115- it('weird state', () => {
116- const state = {
117- loading: false,
118- error: exampleError,
119- loggedIn: true,
120- };
121-
122- Object.freeze(state);
123-
124- expect(reducer(state, {
125- type: 'USER_LOGIN_SUCCESS',
126- }))
127- .toEqual({
128- loading: false,
129- error: null,
130- loggedIn: true,
131- });
132- });
133-});

Old test «loading state» — WITHOUT PAYLOAD.

Called { type: 'USER_LOGIN_SUCCESS' } without the third argument (payload). The old reducer supposedly just set loggedIn: true u loading: false — user data was not saved.

Deleted «weird state»: { loading: false, error: exampleError, loggedIn: true } — nonsensical combination: error present with loggedIn = true. [line 115]

New test — with actual payload:

const payload = {
  user: { name: 'testuser' },
  version: '1.0',
  buildTimestamp: '2024-01-01',
  alertMessage: 'Welcome',
};

Why these exact fields: the reducer at authentication.js:18-27 expects four payload fields — each written directly to state:

Payload field→ state fieldLinePurpose
payload.userstate.userL23user object (name, admin?)
payload.versionstate.versionL24build version
payload.buildTimestampstate.buildTimestampL25when built
payload.alertMessagestate.alertMessageL26system message (MOTD)

The old test passed no payload at all — so it verified none of these fields. The new test checks full correspondence.

loggedIn: trueuser: { name: 'testuser' }. Components check state.user !== null instead of state.loggedIn === true — part of the migration to react-redux hooks.

Section 5: USER_LOGIN_FAILURE — 1 test deleted, 1 changed (lines 110–148)

@@ -133,47 +94,30 @@
133-describe('handles USER_LOGIN_FAILURE', () => {
134- it('loading state', () => {
+describe('USER_LOGIN_FAILURE', () => {
+ it('sets error and clears user/loading', () => {
135 const state = {
136 loading: true,
137 error: null,
138- loggedIn: false,
+ user: { name: 'old' },
139 };
140
141 Object.freeze(state);
142
143 expect(reducer(state, {
144 type: 'USER_LOGIN_FAILURE',
145- payload: {
146- error: exampleError,
147- },
+ payload: { error: exampleError },
148 }))
149 .toEqual({
150 loading: false,
151 error: exampleError,
152- loggedIn: false,
+ user: null,
153 });
154- });
155-
156- it('weird state', () => {
157- const state = {
158- loading: false,
159- error: exampleError,
160- loggedIn: true,
161- };
162-
163- Object.freeze(state);
164-
165- expect(reducer(state, {
166- type: 'USER_LOGIN_FAILURE',
167- payload: {
168- error: exampleError,
169- },
170- }))
171- .toEqual({
172- loading: false,
173- error: exampleError,
174- loggedIn: false,
175- });
176- });
177-});

Similar changes: handles USER_LOGIN_FAILUREUSER_LOGIN_FAILURE, 'weird state' deleted, one test instead of two.

New description: 'sets error and clears user/loading' — explicitly states what happens: error stored, user reset, spinner removed.

loggedIn: falseuser: null. The user field becomes null on error. This is stricter than loggedIn: false — the old approach kept user data in state (just marked as not logged in), the new one fully clears it. Cofromesponds case USER_LOGIN_FAILURE: user: null.

Formatting payload: { error: exampleError } to one line instead of three. Style only.

Section 6: describe('handles USER_LOGOUT') deleted (lines 178–216)

@@ -178,39 +120,12 @@
178-describe('handles USER_LOGOUT', () => {
179- const expectedState = {
180- loading: false,
181- error: null,
182- loggedIn: false,
183- };
184-
185- Object.freeze(expectedState);
186-
187- it('logged in state', () => {
188- const state = {
189- loading: false,
190- error: null,
191- loggedIn: true,
192- };
193-
194- Object.freeze(state);
195-
196- expect(reducer(state, {
197- type: 'USER_LOGOUT',
198- }))
199- .toEqual(expectedState);
200- });
201-
202- it('weird state', () => {
203- const state = {
204- loading: true,
205- error: exampleError,
206- loggedIn: false,
207- };
208-
209- Object.freeze(state);
210-
211- expect(reducer(state, {
212- type: 'USER_LOGOUT',
213- }))
214- .toEqual(expectedState);
215- });
216-});

Deleted entirely (2 tests, ~40 lines).

Verified that USER_LOGOUT resets { loggedIn: true } b { loggedIn: false } u nulls the error.

Why deleted:

  1. Action USER_LOGOUT no longer exists — not exported from authentication.js (imports only USER_LOGIN_BEGIN, USER_LOGIN_FAILURE, USER_LOGIN_SUCCESS).
  2. USER_LOGOUT not in the reducer switch [only 3 case].
  3. Functotionality of logout covered by USER_LOGIN_BEGIN — both reset the state.
  4. The test imported USER_LOGOUT from a nonexistent export — the test was broken.

Historically: USER_LOGOUT was removed from the source commit 7c60c2fbb (2019-07-13, «implemented logout check») — from actions/authentication.js and from the reducer. But the test was not updated — it continued importing and testing USER_LOGOUT for 7 more years. In the new reducer, logout is simply USER_LOGIN_BEGIN (reset to loading).

Concepts

Redux reducer
Pure function: (state, action) → new state. No side effects, no mutations. Object.freeze(state) in the test guarantees this: if the reducer accidentally mutates the passed state, the test fails. [Redux docs]
State shape: loggedIn → user
Old state: { loading, error, loggedIn }. New: { loading, error, user }. loggedIn: boolean only said "yes/no". user: object|null provides the full user object (id, name, locale, ...) — components get all data at once via useSelector(state => state.authentication.user).
Object.freeze
Makes an object immutable. In the reducer test: Object.freeze(state); reducer(state, action); — if the reducer tries to mutate the state (instead of creating a new one), JavaScript throws an error in strict mode. This verifies function purity. [MDN]

Change summary

AspectOld file (216 lines)New file (98 lines)
State shape { loading, error, loggedIn } { loading, error, user }
Initial state Embedded in test, loading: false Constant, matches reducer, loading: true
Number of describe 5 (with prefix handles) 3 (without prefix)
Number of tests 9 4
USER_LOGIN_BEGIN 3 tests (fresh, error, loggedIn — duplicates) 1 test (resets to loading)
USER_LOGIN_SUCCESS 2 tests — without payload 1 test — with payload (user, version, etc.)
USER_LOGIN_FAILURE 2 tests (loading, weird) 1 test (sets error)
USER_LOGOUT 2 test Fully deleted — action does not exist
Unknown action description 'unknown action' — unclear 'unknown action returns current state' — clear

Why 9 tests → 4?

  1. Three describe blocks with 1 test each — each action is tested once with representative input data. No need to test 'fresh', 'error', and 'loggedIn' for each action.
  2. USER_LOGOUT deleted — its functionality is covered by USER_LOGIN_BEGIN (both reset the state). Action does not exist in the reducer.
  3. USER_LOGIN_SUCCESS testir payload — the old test did not check user data (user, version, buildTimestamp, alertMessage), the new one checks all fields.
  4. State shape fromchangedloggedIn: boolean replaced with user: object|null as part of the migration connect()useSelector [react-redux hooks].

— link to code b branch draft43npm on fork, not merged into develop. Code may not be available on GitHub until the PR is merged.