Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4,307 changes: 3,781 additions & 526 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@
"start": "parcel public/index.html --no-cache",
"build": "parcel build -d dist --no-autoinstall --public-url /try public/index.html",
"build:server": "parcel build --target=node ./emulation.ts",
"start:server": "node dist/emulataion"
"start:server": "node dist/emulataion",
"test": "jest --runInBand --detectOpenHandles"
},
"keywords": [
"effector"
],
"author": "YanLobat",
"license": "MIT",
"dependencies": {
"@types/jest": "^26.0.10",
"@types/react": "^16.9.46",
"effector": "^21.2.0",
"effector-react": "^21.0.4",
"firebase": "^7.18.0",
"jest": "^26.4.1",
"react": "^16.9.0",
"react-dom": "^16.9.0"
},
Expand Down
11 changes: 7 additions & 4 deletions src/models/users/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ export const fetchUsersFx = app.createEffect<void, UsersMap>()
export const addUserFx = app.createEffect<FirebaseUser, string[]>()
export const updateUsersTableFx = app.createEffect<{ id: string; tableID: string}, unknown>()
export const deleteUserFx = app.createEffect<string, unknown>()
export const dropUsersFx = app.createEffect<void, unknown>()

export const $firebaseUsers = app.createStore<UsersMap>({});
export const $users = $firebaseUsers.map((fUsers) =>
Object.keys(fUsers).map((id) => fUsers[id])
fUsers === null ? [] : Object.keys(fUsers).map((id) => fUsers[id])
)
export const $usersByEmail = $firebaseUsers.map((fUsers) => {
return Object.keys(fUsers).reduce<UsersMap>((usersByEmail, id) => {
return fUsers === null
? {}
: Object.keys(fUsers).reduce<UsersMap>((usersByEmail, id) => {
const email = fUsers[id].email;
return {
[email]: {
Expand All @@ -28,7 +31,7 @@ export const $usersByEmail = $firebaseUsers.map((fUsers) => {
})

export const $tableUsers = $firebaseUsers.map((fUsers) => {
return Object.keys(fUsers).reduce<TableIDUsersMap>((tableUsers, id) => {
return fUsers === null ? {} : Object.keys(fUsers).reduce<TableIDUsersMap>((tableUsers, id) => {
const tableID = fUsers[id].tableID;
if (tableUsers[tableID] !== undefined) {
tableUsers[tableID].push(fUsers[id]);
Expand All @@ -42,5 +45,5 @@ export const $tableUsers = $firebaseUsers.map((fUsers) => {
})

export const $usersCount = $firebaseUsers.map((fUsers) =>
Object.keys(fUsers).length
fUsers === null ? 0 : Object.keys(fUsers).length
)
8 changes: 7 additions & 1 deletion src/models/users/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
fetchUsersFx,
deleteUserFx,
updateUsersTableFx,
dropUsersFx,
$firebaseUsers,
$usersByEmail,
$tableUsers
Expand All @@ -31,6 +32,7 @@ import {
} from '../app'

const usersRef = database().ref('users/');

usersRef.on('value', (snapshot) => updateUsers(snapshot.val() === null ? {} : snapshot.val()))

addUserFx.use(async (user) => {
Expand All @@ -52,6 +54,10 @@ deleteUserFx.use(async (id) => {
await database().ref('users/'+id).remove()
})

dropUsersFx.use(async () => {
await usersRef.remove()
})

$firebaseUsers
.on([fetchUsersFx.doneData, updateUsers], (_, users) => users)
.on(updateUsersTableFx.done, (users, {params}) => {
Expand Down Expand Up @@ -108,4 +114,4 @@ forward({
}
}),
to: showErrorFx
})
})
86 changes: 86 additions & 0 deletions src/tests/auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { fork, allSettled, Scope } from 'effector'

import '../models/init'
import {app} from '../models/app'
import { signIn, logout, dropUserAuthFx, checkAuthFx, $user } from '../models/auth'
import {dropUsersFx} from '../models/users'

let scope: Scope

beforeEach((done) => {
dropUsersFx().then(() => done())
});

afterEach((done) => {
dropUsersFx().then(() => done())
})

test('should sign in via email and password', async () => {
const expected = {email: 'test@test.com'}
const payload = { password: '123456', ...expected }

scope = fork(app)
await allSettled(signIn, {
scope: scope,
params: payload
})
expect(scope.getState($user)).toMatchObject(expected)
})

test('should sign in via email and password', async () => {
const expected = {email: 'test@test.com'}
const payload = { password: '123456', ...expected }

scope = fork(app)
await allSettled(signIn, {
scope: scope,
params: payload
})
expect(scope.getState($user)).toMatchObject(expected)
})

test('should log out', async () => {
const expected = {email: 'test@test.com'}
const payload = { password: '123456', ...expected }
scope = fork(app)
await allSettled(signIn, {
scope: scope,
params: payload
})
expect(scope.getState($user)).toMatchObject(expected)
await allSettled(signIn, {
scope: scope,
params: payload
})
await allSettled(logout, {
scope: scope,
params: payload.email
})
expect(scope.getState($user)).toMatchObject({email: ''})
})

test('should be still authed if session drops', async () => {
const expected = {email: 'test@test.com'}
const payload = { password: '123456', ...expected }
const dropUserMock = jest.fn()
scope = fork(app, {
handlers: new Map([[dropUserAuthFx, dropUserMock]])
})
await allSettled(signIn, {
scope: scope,
params: payload
})
expect(scope.getState($user)).toMatchObject(expected)
await allSettled(signIn, {
scope: scope,
params: payload
})
await allSettled(logout, {
scope: scope,
params: payload.email
})
await allSettled(checkAuthFx, {
scope: scope
})
expect(dropUserMock).toBeCalledTimes(1)
})
66 changes: 66 additions & 0 deletions src/tests/tables.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// failed to join full table
import { fork, allSettled, Scope } from 'effector'

import '../models/init'
import {app, showErrorFx} from '../models/app'
import { signIn } from '../models/auth'
import { fetchUsersFx, $firebaseUsers, updateUsersTableFx, dropUsersFx, changeUserTable } from '../models/users'
import { $tableCapacity } from '../models/tables'

let scope: Scope

beforeEach((done) => {
dropUsersFx().then(() => done())
});

afterEach((done) => {
dropUsersFx().then(() => done())
})

test('should fail to change table if table is full', async () => {
const emails = [
'ewaters@hotmail.com',
'alias@aol.com',
'temmink@gmail.com',
'rfoley@att.net',
'yruan@me.com',
'parents@gmail.com'
]
const userEmail = 'shrapnull@att.net'
const password = '123456'
const showErrorMock = jest.fn()
scope = fork(app, {
handlers: new Map([[showErrorFx, showErrorMock]])
})
await Promise.all([emails.map(async (email) => {
await allSettled(signIn, {
scope,
params: {email, password}
})
})])
await allSettled(fetchUsersFx, {
scope
})
const users = scope.getState($firebaseUsers)
await Promise.all([Object.keys(users).map(async (id) => {
await allSettled(updateUsersTableFx, {
scope,
params: { id, tableID: 'first-table' }
})
})])

await allSettled(signIn, {
scope: scope,
params: {email: userEmail, password}
})

const allUsers = scope.getState($firebaseUsers)
await allSettled(changeUserTable, {
scope: scope,
params: 'first-table'
})

expect(scope.getState($tableCapacity)).toBeLessThan(Object.keys(allUsers).length)
expect(showErrorMock).toHaveBeenCalledTimes(1)
expect(showErrorMock).toHaveBeenCalledWith('Table is full. You shall not pass!')
})
34 changes: 34 additions & 0 deletions src/tests/users.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// try to sign in same user multiple times
import { fork, allSettled, Scope } from 'effector'

import '../models/init'
import { app } from '../models/app'
import {dropUsersFx} from '../models/users'
import { signIn, $user } from '../models/auth'

beforeEach((done) => {
dropUsersFx().then(() => done())
});

afterEach((done) => {
dropUsersFx().then(() => done())
})

test('should drop previous session if new login from same user', async () => {
const expected = {email: 'test@test.com'}
const payload = { password: '123456', ...expected }

const scope = fork(app)
await allSettled(signIn, {
scope: scope,
params: payload
})
const desktopUser = scope.getState($user)
await allSettled(signIn, {
scope: scope,
params: payload
})
const mobileUser = scope.getState($user)
expect(mobileUser.id === desktopUser.id).toBeFalsy()
})