diff --git a/app/src/routes/login/login.test.ts b/app/src/routes/login/login.test.ts new file mode 100644 index 0000000000..512f4493c5 --- /dev/null +++ b/app/src/routes/login/login.test.ts @@ -0,0 +1,114 @@ +import { createTestingPinia } from '@pinia/testing'; +import { mount } from '@vue/test-utils'; +import { GlobalMountOptions } from '@vue/test-utils/dist/types'; +import { setActivePinia } from 'pinia'; +import { beforeEach, expect, test, vi } from 'vitest'; +import { createI18n } from 'vue-i18n'; + +import vIcon from '@/components/v-icon/v-icon.vue'; +import vImage from '@/components/v-image.vue'; +import vSelect from '@/components/v-select/v-select.vue'; +import MarkdownDirective from '@/directives/markdown'; +import { useAppStore } from '@/stores/app'; +import publicView from '@/views/public/public-view.vue'; +import ContinueAs from './components/continue-as.vue'; +import SsoLinks from './components/sso-links.vue'; +import LdapForm from './components/login-form/ldap-form.vue'; +import LoginForm from './components/login-form/login-form.vue'; +import { useServerStore } from '@/stores/server'; + +import LoginComponent from './login.vue'; + +const i18n = createI18n({ legacy: false }); + +const global: GlobalMountOptions = { + components: { publicView, vSelect, vIcon, vImage }, + directives: { md: MarkdownDirective }, + stubs: { publicView: false }, + plugins: [i18n], +}; + +// silences locale message not found warnings +vi.spyOn(i18n.global, 't').mockImplementation((key: any) => key); + +beforeEach(() => { + setActivePinia( + createTestingPinia({ + createSpy: vi.fn, + stubActions: false, + initialState: { + serverStore: { + info: { + project: { + project_name: null, + project_descriptor: null, + project_logo: null, + project_color: '#6644FF', // ensure Color() usage in public-view doesn't cause error + default_language: null, + public_foreground: null, + public_background: null, + public_note: null, + custom_css: null, + }, + }, + }, + }, + }) + ); +}); + +test('show continue-as when authenticated', () => { + const appStore = useAppStore(); + appStore.authenticated = true; + + const wrapper = mount(LoginComponent, { global, shallow: true }); + + expect(wrapper.findComponent(ContinueAs).exists()).toBe(true); + expect(wrapper.findComponent(LdapForm).exists()).toBe(false); + expect(wrapper.findComponent(LoginForm).exists()).toBe(false); + expect(wrapper.findComponent(SsoLinks).exists()).toBe(false); +}); + +test('show login form and sso links when unauthenticated', () => { + const appStore = useAppStore(); + appStore.authenticated = false; + + const wrapper = mount(LoginComponent, { global, shallow: true }); + + expect(wrapper.findComponent(ContinueAs).exists()).toBe(false); + expect(wrapper.findComponent(LdapForm).exists()).toBe(false); + expect(wrapper.findComponent(LoginForm).exists()).toBe(true); + expect(wrapper.findComponent(SsoLinks).exists()).toBe(true); +}); + +test('show login form when unauthenticated and driver is local', () => { + const appStore = useAppStore(); + appStore.authenticated = false; + + const serverStore = useServerStore(); + serverStore.auth.disableDefault = true; + serverStore.auth.providers = [{ driver: 'local', name: 'localProvider' }]; + + const wrapper = mount(LoginComponent, { global, shallow: true }); + + expect(wrapper.findComponent(ContinueAs).exists()).toBe(false); + expect(wrapper.findComponent(LdapForm).exists()).toBe(false); + expect(wrapper.findComponent(LoginForm).exists()).toBe(true); + expect(wrapper.findComponent(SsoLinks).exists()).toBe(true); +}); + +test('show ldap form when unauthenticated and driver is ldap', () => { + const appStore = useAppStore(); + appStore.authenticated = false; + + const serverStore = useServerStore(); + serverStore.auth.disableDefault = true; + serverStore.auth.providers = [{ driver: 'ldap', name: 'ldapProvider' }]; + + const wrapper = mount(LoginComponent, { global, shallow: true }); + + expect(wrapper.findComponent(ContinueAs).exists()).toBe(false); + expect(wrapper.findComponent(LdapForm).exists()).toBe(true); + expect(wrapper.findComponent(LoginForm).exists()).toBe(false); + expect(wrapper.findComponent(SsoLinks).exists()).toBe(true); +}); diff --git a/app/src/routes/login/login.vue b/app/src/routes/login/login.vue index aac052b109..688c45102e 100644 --- a/app/src/routes/login/login.vue +++ b/app/src/routes/login/login.vue @@ -11,7 +11,7 @@ - + @@ -62,7 +62,7 @@ const providerSelect = computed({ }, set(value: string) { provider.value = value; - driver.value = unref(auth).providers.find((provider) => provider.name === value)?.driver ?? 'default'; + driver.value = unref(auth).providers.find((provider) => provider.name === value)?.driver ?? DEFAULT_AUTH_DRIVER; }, }); diff --git a/app/src/stores/server.ts b/app/src/stores/server.ts index 96b56cc1d3..0a560dc919 100644 --- a/app/src/stores/server.ts +++ b/app/src/stores/server.ts @@ -1,5 +1,5 @@ import api, { replaceQueue } from '@/api'; -import { AUTH_SSO_DRIVERS, DEFAULT_AUTH_PROVIDER } from '@/constants'; +import { AUTH_SSO_DRIVERS, DEFAULT_AUTH_DRIVER, DEFAULT_AUTH_PROVIDER } from '@/constants'; import { i18n } from '@/lang'; import { setLanguage } from '@/lang/set-language'; import formatTitle from '@directus/format-title'; @@ -76,7 +76,11 @@ export const useServerStore = defineStore('serverStore', () => { .map((provider) => ({ text: formatTitle(provider.name), value: provider.name, driver: provider.driver })); if (!auth.disableDefault) { - options.unshift({ text: i18n.global.t('default_provider'), value: DEFAULT_AUTH_PROVIDER, driver: 'default' }); + options.unshift({ + text: i18n.global.t('default_provider'), + value: DEFAULT_AUTH_PROVIDER, + driver: DEFAULT_AUTH_DRIVER, + }); } return options;