Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User store tests #245

Merged
merged 26 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f6f9069
remove duplicate entry for tenat tests in changelog
zhen0 Sep 18, 2020
b292f07
add basic getters tests
zhen0 Sep 18, 2020
b2a0d2d
add correctly failing test for auth0user state
zhen0 Sep 18, 2020
65f2fc9
make it pass
zhen0 Sep 18, 2020
83ab1c5
add timezone getter test
zhen0 Sep 18, 2020
277c9b0
add settings getter test
zhen0 Sep 18, 2020
d6ee30e
add first and last name getters tests - completes getters tests
zhen0 Sep 18, 2020
737e0ec
add setAuth0User mutation test
zhen0 Sep 18, 2020
2b2b3c7
add tests for mutations
zhen0 Sep 18, 2020
d1b9a0a
why does setDefaultTenant send all user, not tenant - needs further i…
zhen0 Sep 18, 2020
cd9aab0
remove console log and add test for setdefaulttenant actions
zhen0 Sep 21, 2020
1bab8f8
add .tenant to first tenant
zhen0 Sep 22, 2020
34b16b4
fix first tenant
zhen0 Sep 22, 2020
52a6eb5
Merge branch 'master' into user-store-tests
zhen0 Sep 22, 2020
7d2014a
don't call setTenant if no tenant
zhen0 Sep 22, 2020
4ce9ad1
remove console logs
zhen0 Sep 22, 2020
e384631
complete user store tests
zhen0 Sep 22, 2020
3116b03
quick fix agents test
zhen0 Sep 22, 2020
ea08e6d
add unit tests to circle yaml
zhen0 Sep 22, 2020
969f8bb
make tests break
zhen0 Sep 22, 2020
c1ba9b9
make it pass again
zhen0 Sep 22, 2020
10ac1e8
remove duplicate?
zhen0 Sep 22, 2020
4032c92
that removes all - add back
zhen0 Sep 22, 2020
3cf2b14
update changelog
zhen0 Sep 22, 2020
4cbe5c8
merge master and fix conflict
zhen0 Sep 25, 2020
04df4d6
Merge branch 'master' into user-store-tests
znicholasbrown Sep 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ commands:
- run: gsutil -m cp -r dist/* gs://<< parameters.BUCKET_NAME >>/

jobs:

unit-test:
# TODO: bump this to large in the future if needed. The default (medium) is resulting in test instabilities (ENOMEM: not enough memory, read)
resource_class: medium
docker:
- image: node:10.16.3
steps:
- prep
- run:
# name: NPM Test:Unit
command: npm run test:unit

build-dev:
resource_class: large
docker:
Expand Down Expand Up @@ -133,6 +145,10 @@ jobs:
workflows:
version: 2

'Run unit tests':
jobs:
- unit-test

'Build and publish development UI artifacts':
jobs:
- docker/publish:
Expand Down
2 changes: 1 addition & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features and Improvements

- None
- Add Vuex user store unit tests and add circleci job to run unit tests on PR [#245](https://github.com/PrefectHQ/ui/pull/245)

### Bugfixes

Expand Down
1 change: 1 addition & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export default {
},
async $route(new_route, old_route) {
if (
new_route?.params?.tenant &&
new_route?.params?.tenant !== old_route?.params?.tenant &&
this.tenant?.slug !== new_route.params.tenant
) {
Expand Down
9 changes: 5 additions & 4 deletions src/store/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const mutations = {
id: null,
email: null,
username: null,
first_name: '',
last_name: '',
Comment on lines +76 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were missing for long enough that I think we should add a templating function for this user state. We can do that in a follow-up PR though.

default_membership_id: null,
memberships: null,
settings: { timezone: '' }
Expand All @@ -96,17 +98,16 @@ const actions = {
const defaultMembershipId = getters['defaultMembershipId']
const defaultTenant = getters['memberships']?.find(
membership => membership.id === defaultMembershipId
).tenant
)?.tenant

const firstTenant =
getters['memberships']?.[0] || rootGetters['tenant/tenants']

if (!defaultMembershipId || rootGetters['api/isServer']) return firstTenant
getters['memberships']?.[0]?.tenant || rootGetters['tenant/tenants']?.[0]
Comment on lines -99 to +104
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good catches on this, hopefully this'll fix some of those edge cases with missing tenants on load.


commit('tenant/setDefaultTenant', defaultTenant || firstTenant, {
root: true
})
},

async getUser({ commit, getters, dispatch }) {
const user = await prefectUser()
commit('user', user)
Expand Down
10 changes: 6 additions & 4 deletions tests/unit/store/agent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@ describe('Agent Vuex Module', () => {
})
})

describe('mutationss', () => {
describe('mutations', () => {
let store = new Vuex.Store({
state: initialAgentState(),
getters: agent.getters,
mutations: agent.mutations
})

it('should add new agents when the setAgents mutation is called', () => {
it('should add new agents, and update seconds since last query, when the setAgents mutation is called', () => {
expect(store.getters.agents).toBe(null)
store.commit('setAgents', { id: '12345' })
expect(store.getters.agents).toEqual({ id: '12345' })
store.commit('setAgents', [{ id: '12345' }])
expect(store.getters.agents).toEqual([
{ id: '12345', secondsSinceLastQuery: 0 }
])
Comment on lines +62 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this introduce a potential race-conditional test failure? There should never be a full second between lines 61 and 63 but...

})
})
})
291 changes: 291 additions & 0 deletions tests/unit/store/user.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
import user from '@/store/user'
import { createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('@/middleware/prefectAuth', () => {
null
})

jest.mock('@/middleware/prefectAuth', () => {
return {
prefectUser: jest.fn()
}
})
import { prefectUser } from '@/middleware/prefectAuth'

describe('user Vuex Module', () => {
const initialState = () => {
return {
user: {
id: null,
email: null,
username: null,
default_membership_id: null,
memberships: null,
first_name: '',
last_name: '',
settings: {
timezone: ''
}
},
auth0User: {
name: null,
email: null,
picture: null
},
userIsSet: false
}
}

const userState = () => {
return {
user: {
id: '12345',
email: 'test@test.com',
username: 'test123',
default_membership_id: '5678',
memberships: [
{
id: '1112131415',
role: 'USER',
tenant: { id: 'yyyyy', name: 'test2', slug: 'test2' }
},
{
id: '5678',
role: 'TENANT_ADMIN',
tenant: { id: 'xxx', name: 'test1', slug: 'test1' }
}
],
first_name: 'first',
last_name: 'last',
settings: {
timezone: 'utc'
}
},
auth0User: {
name: 'testtest',
email: 'test@test.com',
picture: 'linktopicture'
},
userIsSet: true
}
}

describe('State', () => {
test('userIsSet should initally be set to false', () => {
const state = user.state
expect(state.userIsSet).toBe(false)
})
test('user details should initally be empty', () => {
const state = user.state
expect(state.user).toEqual({
id: null,
email: null,
username: null,
default_membership_id: null,
memberships: null,
first_name: '',
last_name: '',
settings: {
timezone: ''
}
})
})
test('auth0User details should initally be empty', () => {
const state = user.state
expect(state.auth0User).toEqual({
name: null,
email: null,
picture: null
})
})
})

describe('getters', () => {
let store
store = new Vuex.Store({
state: userState(),
getters: user.getters,
mutations: user.mutations
})
test('user should return user details', () => {
expect(store.getters.user).toBe(store.state.user)
})
test('userIsSet should initially return false', () => {
expect(store.getters.userIsSet).toBe(true)
})
test('defaultMembershipId should return membership id if user membership id is set', () => {
expect(store.getters.defaultMembershipId).toEqual('5678')
})
test('auth0user should return auth0user details', () => {
expect(store.getters.auth0User).toEqual({
name: 'testtest',
email: 'test@test.com',
picture: 'linktopicture'
})
})
test('timezone should return user timezone', () => {
expect(store.getters.timezone).toEqual('utc')
})
test('settings should return user settings', () => {
expect(store.getters.settings).toEqual({
timezone: 'utc'
})
})
test('firstName should return user first name', () => {
expect(store.getters.firstName).toEqual('first')
})
test('lastName should return user last name', () => {
expect(store.getters.lastName).toEqual('last')
})
test('memberships should return memberships array', () => {
expect(store.getters.memberships).toEqual(userState().user.memberships)
})
})

describe('mutations', () => {
let store
beforeEach(() => {
store = new Vuex.Store({
state: initialState(),
getters: user.getters,
mutations: user.mutations
})
})

test('user mutation sets the user and updates user is set', () => {
expect(store.getters.user).toEqual(initialState().user)
expect(store.getters.userIsSet).toBe(false)
store.commit('user', userState().user)
expect(store.getters.user).toEqual(userState().user)
expect(store.getters.user.email).toEqual('test@test.com')
expect(store.getters.userIsSet).toBe(true)
})
test('setAuth0User mutation sets the auth0 user', () => {
expect(store.getters.auth0User).toEqual(initialState().auth0User)
store.commit('setAuth0User', userState().auth0User)
expect(store.getters.auth0User).toEqual(userState().auth0User)
expect(store.getters.auth0User.email).toEqual('test@test.com')
})
test('setUsetSettings mutation sets the user settings', () => {
expect(store.getters.user).toEqual(initialState().user)
store.commit('setUserSettings', userState().user.settings)
expect(store.getters.settings).toEqual(userState().user.settings)
expect(store.getters.timezone).toEqual('utc')
})
test('unsetUser mutation un-sets the user and updates user is set', () => {
store.commit('user', userState().user)
expect(store.getters.user).toEqual(userState().user)
expect(store.getters.user.email).toEqual('test@test.com')
expect(store.getters.userIsSet).toBe(true)
store.commit('unsetUser')
expect(store.getters.user).toEqual(initialState().user)
expect(store.getters.userIsSet).toBe(false)
})
test('unsetAuth0User mutation un-sets the auth0 user', () => {
store.commit('setAuth0User', userState().auth0User)
expect(store.getters.auth0User).toEqual(userState().auth0User)
expect(store.getters.auth0User.email).toEqual('test@test.com')
store.commit('unsetAuth0User')
expect(store.getters.auth0User).toEqual(initialState().auth0User)
expect(store.getters.auth0User.email).toEqual(null)
})
test('setUserDefaultMembershipId mutation sets the default membership', () => {
expect(store.getters.defaultMembershipId).toEqual(null)
store.commit('setUserDefaultMembershipId', '5678')
expect(store.getters.defaultMembershipId).toEqual('5678')
})
})

describe('actions', () => {
describe('setDefaultTenant action', () => {
let store
beforeEach(() => {
// Mock the mutations and actions from other stores
// that we don't want to
// test here
user.mutations['tenant/setDefaultTenant'] = jest.fn()
store = new Vuex.Store({
state: userState(),
getters: {
...user.getters,
'tenant/tenants': () => [
{ id: '3333', name: 'test33', slug: 'test33' }
]
},
mutations: user.mutations,
actions: user.actions
})
})
it('sets the default tenant according to the default membership id if available', () => {
expect(store.getters.defaultMembershipId).toEqual('5678')
store.dispatch('setDefaultTenant')
expect(
//First argument passed is the user state object, second is the default tenant to set
user.mutations['tenant/setDefaultTenant'].mock.calls[0][1]
).toEqual({
id: 'xxx',
name: 'test1',
slug: 'test1'
})
})
it('sets the default tenant according to the first tenant in the memberships array if there is no tenant matching the default membership id', async () => {
expect(store.getters.defaultMembershipId).toEqual('5678')
store.commit('setUserDefaultMembershipId', '2222')
expect(store.getters.defaultMembershipId).toEqual('2222')
await store.dispatch('setDefaultTenant')
expect(
//First argument passed is the user state, second is the default tenant to set
user.mutations['tenant/setDefaultTenant'].mock.calls[0][1]
).toEqual({ id: 'yyyyy', name: 'test2', slug: 'test2' })
})
it('sets the default tenant according to the first tenant in the tenants store tenants array if there are no memberships', async () => {
store.commit('unsetUser')
expect(store.getters.memberships).toEqual(null)
await store.dispatch('setDefaultTenant')
expect(
//First argument passed is the user state, second is the default tenant to set
user.mutations['tenant/setDefaultTenant'].mock.calls[0][1]
).toEqual({ id: '3333', name: 'test33', slug: 'test33' })
})
})

describe('getUser action', () => {
let store
beforeEach(() => {
// Mock the mutations and actions from other stores
// that we don't want to
// test here
user.mutations['tenant/setDefaultTenant'] = jest.fn()
store = new Vuex.Store({
state: initialState(),
getters: user.getters,
mutations: user.mutations,
actions: user.actions
})
})
it('calls prefectUser', async () => {
await store.dispatch('getUser')
expect(prefectUser).toBeCalled()
})
it('set the user', async () => {
expect(store.getters.user).toEqual(initialState().user)
prefectUser.mockReturnValueOnce(userState().user)
await store.dispatch('getUser')
expect(store.getters.user).toEqual(userState().user)
})
it('calls setDefaultTenant', async () => {
await store.dispatch('getUser')
expect(user.mutations['tenant/setDefaultTenant']).toBeCalled()
})
it('returns the user', async () => {
prefectUser.mockReturnValueOnce(userState().user)
const returnedUser = await store.dispatch('getUser')
expect(returnedUser).toEqual(userState().user)
})
})
})
})