⚠️ Versión preliminar de la documentación. Puede contener erratas e imprecisiones.
EN | DE | RU | FR | ES

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

Fecha: 2026-05-08

Commits comparados:

Reductor (versión antigua y nueva — idéntico): reducers/authentication.js (40 líneas) — no ha cambiado entre los commits (creado el 28/02/2019).

Nota: Los enlaces al nuevo código (SHA e67067aa7) apuntan al fork MaurerAnton/projectforge (rama draft43npm). Los enlaces al código antiguo (SHA 9ed5fbe0f) apuntan al repositorio principal micromata/projectforge (develop).

Sección 1: Estado inicial (líneas 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 extraído a una constante.

Antes: el objeto estaba hardcodeado en el test it('initial state') como { loading: false, error: null, loggedIn: false }.

Ahora: constante initialState, que además coincide con el initialState del propio reductor [reducers/authentication.js:3].

2. loading: falseloading: true.

Init antiguo: loading: false — el usuario no ha iniciado sesión, el spinner no está girando.

Init nuevo: loading: true — al cargar la aplicación se ejecuta inmediatamente loadUserStatus(), que verifica la sesión. El estado inicial es «buscando al usuario». Este cambio corresponde al reductor en el commit 8b3b44be7 (18/03/2020).

3. loggedIn: falseuser: null. CAMBIO PRINCIPAL EN LA ESTRUCTURA DEL ESTADO (STATE SHAPE).

{/* Forma antigua */}
{ loading: boolean, error: string|null, loggedIn: boolean }
{/* Nueva forma */}
{ loading: boolean, error: string|null, user: object|null }

Sección 2: Acción desconocida (líneas 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. Descripción explícita: «el reductor no reconoce esta acción → devuelve el estado actual sin cambios». El test antiguo no especificaba qué se estaba verificando exactamente.

Cambio en el estado de entrada: el test antiguo verificaba { loading: true, loggedIn: false }, el nuevo — { loading: false, user: { name: 'test' } }. Se ha elegido un estado con usuario logueado para comprobar que user no se establece en null ante una acción desconocida, garantizando así el comportamiento de función pura [Redux reducers].

Formato: la llamada antigua se dividía en 3 líneas, la nueva ocupa 1 sola línea. Se trata únicamente de estilo/codificación.

Object.freeze(state) [MDN] — protección contra mutaciones: si el reductor modifica accidentalmente el state recibido (violando el principio de pureza), el test fallará con un error.

Sección 3: USER_LOGIN_BEGIN — eliminados 2 tests, 1 modificado (líneas 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'). Se ha eliminado la palabra handles — en el nuevo estilo todos los describe nombran la acción literalmente. [archivo nuevo]

Eliminados 2 de 3 tests.

«fresh state» — verificaba la transición de { loading: false, error: null, ... } a { loading: true, error: null, ... }. [línea 34]

«error state» — verificaba la transición de { loading: false, error: exampleError, ... } a { loading: true, error: null, ... }. [línea 53]

Ambos tests verificaban lo mismo: error se establece a null, loading pasa a true. La única diferencia era el error inicial (null vs string). Es redundante, con un solo test se cubren ambos casos.

«loggedIn state» — entrada: { loading: false, error: null, loggedIn: false } — coincide completamente con «fresh state». Probablemente un error tipográfico (debería ser loggedIn: true). [línea 72]

Un nuevo test: 'resets to loading with null user' [archivo nuevo]

Sección 4: USER_LOGIN_SUCCESS — eliminado 1 test, 1 modificado (líneas 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-});

Test antiguo «loading state» — SIN PAYLOAD.

Llamaba a { type: 'USER_LOGIN_SUCCESS' } sin el tercer argumento (payload). El reductor antiguo supuestamente solo establecía loggedIn: true y loading: false — los datos del usuario no se conservaban.

Eliminado «weird state»: { loading: false, error: exampleError, loggedIn: true } — combinación sin sentido: hay error cuando loggedIn = true. [línea 115]

Nuevo test — con payload real:

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

Por qué exactamente estos campos: el reductor en authentication.js:18-27 espera un payload con cuatro campos — cada uno se escribe directamente en el estado:

Campo payload→ campo estadoLíneaPropósito
payload.userstate.userL23objeto de usuario (nombre, ¿es admin?...)
payload.versionstate.versionL24versión de la build
payload.buildTimestampstate.buildTimestampL25cuándo se compiló
payload.alertMessagestate.alertMessageL26mensaje del sistema (MOTD)

El test antiguo no pasaba payload en absoluto, por lo que no verificaba ninguno de estos campos. El nuevo test comprueba la correspondencia completa.

loggedIn: trueuser: { name: 'testuser' }. Los componentes verifican state.user !== null en lugar de state.loggedIn === true — parte de la migración a react-redux hooks.

Sección 5: USER_LOGIN_FAILURE — eliminado 1 test, 1 modificado (líneas 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-});

Cambios análogos: handles USER_LOGIN_FAILUREUSER_LOGIN_FAILURE, eliminado «weird state», un solo test en lugar de dos.

Nueva descripción: 'sets error and clears user/loading' — describe explícitamente lo que ocurre: se conserva el error, se reinicia el usuario, se detiene el spinner.

loggedIn: falseuser: null. El campo user pasa a null en caso de error. Esto es más estricto que loggedIn: false — el enfoque antiguo dejaba los datos del usuario en el estado (solo marcaba que no estaba logueado), el nuevo los elimina por completo. Coincide con case USER_LOGIN_FAILURE: user: null.

Formato de payload: { error: exampleError } en una sola línea en lugar de tres. Solo es cuestión de estilo.

Sección 6: Eliminado describe('handles USER_LOGOUT') (líneas 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-});

Eliminado por completo (2 tests, ~40 líneas).

Verificaba que USER_LOGOUT reseteaba { loggedIn: true } a { loggedIn: false } y establecía el error en null.

Por qué se eliminó:

  1. La acción USER_LOGOUT ya no existe — no se exporta desde authentication.js (solo se importan USER_LOGIN_BEGIN, USER_LOGIN_FAILURE, USER_LOGIN_SUCCESS).
  2. USER_LOGOUT no aparece en el switch del reductor [solo 3 case].
  3. La funcionalidad de logout está cubierta por USER_LOGIN_BEGIN — ambos reinician el estado.
  4. El test importaba USER_LOGOUT de un export inexistente — el test estaba roto.

Contexto histórico: USER_LOGOUT fue eliminado del código fuente en commit 7c60c2fbb (13/07/2019, «implemented logout check») — tanto en actions/authentication.js como en el reductor. Pero no se actualizó el test — siguió importando y probando USER_LOGOUT durante otros 7 años. En el nuevo enfoque, el logout se maneja simplemente con USER_LOGIN_BEGIN (reseteo a loading).

Conceptos clave

Redux reducer
Función pura: (state, action) → nuevo estado. Sin efectos secundarios, sin mutaciones. Object.freeze(state) en el test lo garantiza: si el reductor modifica accidentalmente el state recibido, el test fallará. [Redux docs]
Estructura del estado (state shape): loggedIn → user
Estado antiguo: { loading, error, loggedIn }. Nuevo: { loading, error, user }. loggedIn: boolean solo indicaba «sí/no». user: object|null proporciona el objeto completo del usuario (id, name, locale, ...) — los componentes obtienen todos los datos directamente mediante useSelector(state => state.authentication.user).
Object.freeze
Hace el objeto inmutable. En tests de reductores: Object.freeze(state); reducer(state, action); — si el reductor intenta modificar el estado (en lugar de crear uno nuevo), JavaScript lanzará un error en strict mode. Es una verificación de la pureza de la función. [MDN]

Resumen de cambios

AspectoArchivo antiguo (216 líneas)Archivo nuevo (98 líneas)
Estructura del estado { loading, error, loggedIn } { loading, error, user }
Estado inicial Incrustado en el test, loading: false Constante, coincide con el reductor, loading: true
Cantidad de describe 5 (con prefijo handles) 3 (sin prefijo)
Cantidad de tests 9 4
USER_LOGIN_BEGIN 3 tests (fresh, error, loggedIn — duplicados) 1 test (resets to loading)
USER_LOGIN_SUCCESS 2 tests — sin payload 1 test — con payload (user, version, etc.)
USER_LOGIN_FAILURE 2 tests (loading, weird) 1 test (sets error)
USER_LOGOUT 2 tests Eliminado por completo — la acción no existe
Descripción de unknown action 'unknown action' — poco claro 'unknown action returns current state' — claro

¿Por qué se pasó de 9 a 4 tests?

  1. Tres describe con 1 test cada uno — cada acción se verifica una sola vez con datos de entrada representativos. No es necesario probar «estado inicial», «con error» y «logueado» para cada acción.
  2. USER_LOGOUT eliminado — su funcionalidad está cubierta por USER_LOGIN_BEGIN (ambos reinician el estado). La acción no existe en el reductor.
  3. USER_LOGIN_SUCCESS prueba el payload — el test antiguo no verificaba los datos del usuario (user, version, buildTimestamp, alertMessage), el nuevo comprueba todos los campos.
  4. Estructura del estado modificadaloggedIn: boolean se ha reemplazado por user: object|null como parte de la migración de connect() a useSelector [react-redux hooks].

— enlace al código en la rama draft43npm del fork, que aún no se ha fusionado con develop. El código podría no estar disponible en GitHub hasta que se fusione el PR.