From 7c3de89df5a9c1a06f621878779314602a2d02ee Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 28 Feb 2020 11:51:35 -0500 Subject: [PATCH] Improve LoginPage unit tests Uses a single test for the entire page, and more focused tests for each permutation of BasicLoginForm/DisabledLoginForm. This greatly reduces the snapshot size. Also adds tests for API calls. --- .../__snapshots__/login_page.test.tsx.snap | 722 +++--------------- .../authentication/login/login_page.test.tsx | 171 ++++- 2 files changed, 231 insertions(+), 662 deletions(-) diff --git a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap index 87bfba689ca8a..c1b8202e2f3f3 100644 --- a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap @@ -1,582 +1,120 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`LoginPage disabled form states renders as expected when a connection to ES is not available 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - } - title={ - - } - /> - - -
-
+ + } + title={ + + } +/> `; exports[`LoginPage disabled form states renders as expected when an unknown loginState layout is provided 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - } - title={ - - } - /> - - -
-
+ + } + title={ + + } +/> `; -exports[`LoginPage disabled form states renders as expected when loginAssistanceMessage is set 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - - -
-
-`; - -exports[`LoginPage disabled form states renders as expected when secure cookies are required but not present 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - } - title={ - - } - /> - - -
-
+exports[`LoginPage disabled form states renders as expected when secure connection is required but not present 1`] = ` + + } + title={ + + } +/> `; exports[`LoginPage disabled form states renders as expected when xpack is not available 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - } - title={ - - } - /> - - -
-
+ + } + title={ + + } +/> `; exports[`LoginPage enabled form state renders as expected 1`] = ` -
-
-
- - - - - -

- -

-
- -

- -

-
- -
-
-
- - - - - -
-
+ `; exports[`LoginPage enabled form state renders as expected when info message is set 1`] = ` + +`; + +exports[`LoginPage enabled form state renders as expected when loginAssistanceMessage is set 1`] = ` + +`; + +exports[`LoginPage page renders as expected 1`] = `
@@ -637,66 +175,10 @@ exports[`LoginPage enabled form state renders as expected when info message is s diff --git a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx index ac17dfa5870ef..294434cd08ebc 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx @@ -11,6 +11,7 @@ import { nextTick } from 'test_utils/enzyme_helpers'; import { LoginState } from './login_state'; import { LoginPage } from './login_page'; import { coreMock } from '../../../../../../src/core/public/mocks'; +import { DisabledLoginForm, BasicLoginForm } from './components'; const createLoginState = (options?: Partial) => { return { @@ -21,14 +22,63 @@ const createLoginState = (options?: Partial) => { }; describe('LoginPage', () => { + // mock a minimal subset of the HttpSetup + const httpMock = { + get: jest.fn(), + addLoadingCountSource: jest.fn(), + } as any; + const resetHttpMock = () => { + httpMock.get.mockReset(); + httpMock.addLoadingCountSource.mockReset(); + }; + + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { href: 'http://some-host/bar', protocol: 'http' }, + writable: true, + }); + }); + + beforeEach(() => { + resetHttpMock(); + }); + + afterAll(() => { + delete (window as any).location; + }); + + describe('page', () => { + it('renders as expected', async () => { + const coreStartMock = coreMock.createStart(); + httpMock.get.mockResolvedValue(createLoginState()); + + const wrapper = shallow( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot + }); + + expect(wrapper).toMatchSnapshot(); + }); + }); + describe('disabled form states', () => { - it('renders as expected when secure cookies are required but not present', async () => { + it('renders as expected when secure connection is required but not present', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue(createLoginState()); + httpMock.get.mockResolvedValue(createLoginState()); const wrapper = shallow( { wrapper.update(); }); - expect(coreStartMock.http.get).toHaveBeenCalledTimes(1); - expect(coreStartMock.http.get).toHaveBeenCalledWith('/internal/security/login_state'); - - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(DisabledLoginForm)).toMatchSnapshot(); }); it('renders as expected when a connection to ES is not available', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue( - createLoginState({ layout: 'error-es-unavailable' }) - ); + httpMock.get.mockResolvedValue(createLoginState({ layout: 'error-es-unavailable' })); const wrapper = shallow( { wrapper.update(); }); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(DisabledLoginForm)).toMatchSnapshot(); }); it('renders as expected when xpack is not available', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue( - createLoginState({ layout: 'error-xpack-unavailable' }) - ); + httpMock.get.mockResolvedValue(createLoginState({ layout: 'error-xpack-unavailable' })); const wrapper = shallow( { wrapper.update(); }); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(DisabledLoginForm)).toMatchSnapshot(); }); it('renders as expected when an unknown loginState layout is provided', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue( + httpMock.get.mockResolvedValue( createLoginState({ layout: 'error-asdf-asdf-unknown' as any }) ); const wrapper = shallow( { wrapper.update(); }); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(DisabledLoginForm)).toMatchSnapshot(); }); + }); - it('renders as expected when loginAssistanceMessage is set', async () => { + describe('enabled form state', () => { + it('renders as expected', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue(createLoginState()); + httpMock.get.mockResolvedValue(createLoginState()); const wrapper = shallow( ); @@ -131,31 +176,66 @@ describe('LoginPage', () => { await act(async () => { await nextTick(); wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot }); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(BasicLoginForm)).toMatchSnapshot(); }); - }); - describe('enabled form state', () => { - beforeAll(() => { - Object.defineProperty(window, 'location', { - value: { href: 'http://some-host/bar', protocol: 'http' }, - writable: true, + it('renders as expected when info message is set', async () => { + const coreStartMock = coreMock.createStart(); + httpMock.get.mockResolvedValue(createLoginState()); + window.location.href = 'http://some-host/bar?msg=SESSION_EXPIRED'; + + const wrapper = shallow( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot }); + + expect(wrapper.find(BasicLoginForm)).toMatchSnapshot(); }); - afterAll(() => { - delete (window as any).location; + it('renders as expected when loginAssistanceMessage is set', async () => { + const coreStartMock = coreMock.createStart(); + httpMock.get.mockResolvedValue(createLoginState()); + + const wrapper = shallow( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot + }); + + expect(wrapper.find(BasicLoginForm)).toMatchSnapshot(); }); + }); - it('renders as expected', async () => { + describe('API calls', () => { + it('GET login_state success', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue(createLoginState()); + httpMock.get.mockResolvedValue(createLoginState()); const wrapper = shallow( { wrapper.update(); }); - expect(wrapper).toMatchSnapshot(); + expect(httpMock.addLoadingCountSource).toHaveBeenCalledTimes(1); + expect(httpMock.get).toHaveBeenCalledTimes(1); + expect(httpMock.get).toHaveBeenCalledWith('/internal/security/login_state'); + expect(coreStartMock.fatalErrors.add).not.toHaveBeenCalled(); }); - it('renders as expected when info message is set', async () => { + it('GET login_state failure', async () => { const coreStartMock = coreMock.createStart(); - coreStartMock.http.get.mockResolvedValue(createLoginState()); - window.location.href = 'http://some-host/bar?msg=SESSION_EXPIRED'; + const error = Symbol(); + httpMock.get.mockRejectedValue(error); const wrapper = shallow( { wrapper.update(); }); - expect(wrapper).toMatchSnapshot(); + expect(httpMock.addLoadingCountSource).toHaveBeenCalledTimes(1); + expect(httpMock.get).toHaveBeenCalledTimes(1); + expect(httpMock.get).toHaveBeenCalledWith('/internal/security/login_state'); + expect(coreStartMock.fatalErrors.add).toHaveBeenCalledTimes(1); + expect(coreStartMock.fatalErrors.add).toHaveBeenCalledWith(error); }); }); });