mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 17:18:33 +00:00
Remember the last view used per table, per user
This commit is contained in:
parent
f3e21464c5
commit
feff125f25
8 changed files with 651 additions and 9 deletions
changelog/entries/unreleased/feature
web-frontend
modules/database
test
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Remember the last used view, per table, per user",
|
||||
"issue_number": 1273,
|
||||
"bullet_points": [],
|
||||
"created_at": "2023-09-01"
|
||||
}
|
|
@ -131,16 +131,17 @@ export default {
|
|||
data.view = undefined
|
||||
|
||||
// Because we do not have a dashboard for the table yet we're going to redirect to
|
||||
// the first available view.
|
||||
const firstView = store.getters['view/first']
|
||||
if (viewId === null && firstView !== null) {
|
||||
const firstViewType = app.$registry.get('view', firstView.type)
|
||||
// the last visited or the first available view.
|
||||
const viewToUse = store.getters['view/defaultOrFirst']
|
||||
|
||||
if (viewId === null && viewToUse !== null) {
|
||||
const firstViewType = app.$registry.get('view', viewToUse.type)
|
||||
// If the view is deactivated, it's not possible to open the view because it will
|
||||
// put the user in an unrecoverable state. Therefore, it's better to not select a
|
||||
// view, so that the user can choose which they want to select in the top left
|
||||
// corner.
|
||||
if (!firstViewType.isDeactivated(data.database.workspace.id)) {
|
||||
viewId = firstView.id
|
||||
viewId = viewToUse.id
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { StoreItemLookupError } from '@baserow/modules/core/errors'
|
||||
import { uuid } from '@baserow/modules/core/utils/string'
|
||||
import { uuid, isSecureURL } from '@baserow/modules/core/utils/string'
|
||||
import { fitInCookie } from '@baserow/modules/database/utils/view'
|
||||
import ViewService from '@baserow/modules/database/services/view'
|
||||
import FilterService from '@baserow/modules/database/services/filter'
|
||||
import DecorationService from '@baserow/modules/database/services/decoration'
|
||||
|
@ -69,6 +70,7 @@ export const state = () => ({
|
|||
loading: false,
|
||||
items: [],
|
||||
selected: {},
|
||||
defaultViewId: null,
|
||||
})
|
||||
|
||||
export const mutations = {
|
||||
|
@ -209,6 +211,21 @@ export const mutations = {
|
|||
SET_SORT_LOADING(state, { sort, value }) {
|
||||
sort._.loading = value
|
||||
},
|
||||
/**
|
||||
* Data for defaultViewId for $cookies:
|
||||
* [
|
||||
* {table_id: table1Id, id: view1Id},
|
||||
* {table_id: table2Id, id: view2Id},
|
||||
* . . .
|
||||
* ]
|
||||
* Data for defaultViewId for Vuex store:
|
||||
* {
|
||||
* defaultViewId: view1Id,
|
||||
* }
|
||||
*/
|
||||
SET_DEFAULT_VIEW(state, data) {
|
||||
state.defaultViewId = data
|
||||
},
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
|
@ -238,6 +255,9 @@ export const actions = {
|
|||
})
|
||||
commit('SET_ITEMS', data)
|
||||
commit('SET_LOADING', false)
|
||||
|
||||
// Get the default view for the table.
|
||||
dispatch('getDefaultView', { tableId: table.id })
|
||||
} catch (error) {
|
||||
commit('SET_ITEMS', [])
|
||||
commit('SET_LOADING', false)
|
||||
|
@ -413,6 +433,10 @@ export const actions = {
|
|||
*/
|
||||
select({ commit, dispatch }, view) {
|
||||
commit('SET_SELECTED', view)
|
||||
|
||||
// Set the default view for the table.
|
||||
dispatch('setDefaultView', { view })
|
||||
|
||||
dispatch(
|
||||
'undoRedo/updateCurrentScopeSet',
|
||||
DATABASE_ACTION_SCOPES.view(view.id),
|
||||
|
@ -447,6 +471,74 @@ export const actions = {
|
|||
}
|
||||
return dispatch('select', view)
|
||||
},
|
||||
/**
|
||||
* Gets the default view from cookies (if it exists) OR the first view
|
||||
* otherwise, sets it in Vuex store
|
||||
*/
|
||||
getDefaultView({ commit, getters }, { tableId }) {
|
||||
try {
|
||||
const defaultViewIdData = this.$cookies.get('defaultViewId') || []
|
||||
const foundView = defaultViewIdData.find(
|
||||
(view) => view.table_id === tableId
|
||||
)
|
||||
|
||||
if (foundView) {
|
||||
const view = getters.get(foundView.id)
|
||||
commit('SET_DEFAULT_VIEW', view?.id)
|
||||
} else {
|
||||
commit('SET_DEFAULT_VIEW', null)
|
||||
}
|
||||
} catch (error) {
|
||||
// in case of any exception, set default view to null, this should load the
|
||||
// first view:
|
||||
commit('SET_DEFAULT_VIEW', null)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the default view for table in cookies and in Vuex store
|
||||
*/
|
||||
setDefaultView({ commit }, { view }) {
|
||||
const defaultViewIdData = this.$cookies.get('defaultViewId') || []
|
||||
|
||||
try {
|
||||
// Find the existing object with the same table_id, if it exists
|
||||
const existingViewIndex = defaultViewIdData.findIndex(
|
||||
(obj) => obj.table_id === view.table_id
|
||||
)
|
||||
|
||||
if (existingViewIndex !== -1) {
|
||||
// If existingView is found, remove it from the array
|
||||
const existingView = defaultViewIdData.splice(existingViewIndex, 1)[0]
|
||||
// Update the id of the existing object
|
||||
existingView.id = view.id
|
||||
// Add the existingView back to the end of the array
|
||||
defaultViewIdData.push(existingView)
|
||||
} else {
|
||||
if (view.id === view.slug) {
|
||||
// we are viewing a public view so ignore for the purposes of setting
|
||||
// a default view
|
||||
return
|
||||
}
|
||||
// Add a new object for the table_id
|
||||
defaultViewIdData.push({ table_id: view.table_id, id: view.id })
|
||||
}
|
||||
} catch (error) {
|
||||
const defaultViewIdData = []
|
||||
// in case of any exception, set default view to the current view:
|
||||
defaultViewIdData.push({ table_id: view.table_id, id: view.id })
|
||||
} finally {
|
||||
// Limit the number of views to remember (based on the max. cookie size)
|
||||
const fittedList = fitInCookie('defaultViewId', defaultViewIdData)
|
||||
const secure = isSecureURL(this.$config.PUBLIC_WEB_FRONTEND_URL)
|
||||
this.$cookies.set('defaultViewId', fittedList, {
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24 * 365, // 1 year
|
||||
sameSite: 'lax',
|
||||
secure,
|
||||
})
|
||||
commit('SET_DEFAULT_VIEW', view.id)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Changes the loading state of a specific filter.
|
||||
*/
|
||||
|
@ -903,6 +995,13 @@ export const getters = {
|
|||
.sort((a, b) => a.order - b.order)
|
||||
return items.length > 0 ? items[0] : null
|
||||
},
|
||||
// currently only used during unit tests:
|
||||
defaultId: (state) => {
|
||||
return state.defaultViewId
|
||||
},
|
||||
defaultOrFirst: (state, getters) => {
|
||||
return getters.get(state.defaultViewId) || getters.first
|
||||
},
|
||||
getAll(state) {
|
||||
return state.items
|
||||
},
|
||||
|
|
|
@ -300,3 +300,45 @@ export function getFilters(rootGetters, viewId) {
|
|||
|
||||
return filters
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the size of a UTF-8 encoded string in bytes - computes the size
|
||||
* of a string in UTF-8 encoding and utilizes the TextEncoder API if available.
|
||||
*
|
||||
* Using TextEncoder is preferred in Modern Browsers and Node.js Supported
|
||||
* environments because it provides a more efficient and accurate way to encode
|
||||
* strings into UTF-8 bytes and directly calculate the byte size of the encoded
|
||||
* string.
|
||||
*
|
||||
* In some older web browsers or environments where TextEncoder may not be available
|
||||
* (such as SSR where certain browser APIs are absent), it falls back to a less
|
||||
* accurate method and simply returns the length of the string.
|
||||
*/
|
||||
export function utf8ByteSize(str) {
|
||||
// Use TextEncoder if available (modern browsers and Node.js)
|
||||
if (typeof TextEncoder !== 'undefined') {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(str)
|
||||
return data.length
|
||||
} else {
|
||||
// Fallback for older browsers (may not be as accurate)
|
||||
return str.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the size of a cookie's value by removing elements from an array
|
||||
* until it fits within the maximum allowed cookie size.
|
||||
*/
|
||||
export function fitInCookie(name, list) {
|
||||
const result = []
|
||||
for (let i = list.length - 1; i >= 0; i--) {
|
||||
result.unshift(list[i])
|
||||
const serialized = encodeURIComponent(JSON.stringify(result))
|
||||
if (utf8ByteSize(serialized) > 4096) {
|
||||
result.shift() // Remove the last added item as it caused the size to exceed the limit
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
16
web-frontend/test/fixtures/mockServer.js
vendored
16
web-frontend/test/fixtures/mockServer.js
vendored
|
@ -43,8 +43,20 @@ export class MockServer {
|
|||
return { application, workspace }
|
||||
}
|
||||
|
||||
createTable() {
|
||||
return { id: 1, name: 'Test Table 1' }
|
||||
async createAppAndWorkspaceWithMultipleTables(tables) {
|
||||
const workspace = createWorkspace(this.mock, {})
|
||||
this.loadPermissions(workspace)
|
||||
const application = createApplication(this.mock, {
|
||||
workspaceId: workspace.id,
|
||||
tables,
|
||||
})
|
||||
await this.store.dispatch('workspace/fetchAll')
|
||||
await this.store.dispatch('application/fetchAll')
|
||||
return { application, workspace }
|
||||
}
|
||||
|
||||
createTable(id = 1, name = 'Test Table 1') {
|
||||
return { id, name }
|
||||
}
|
||||
|
||||
createGridView(
|
||||
|
|
|
@ -96,7 +96,7 @@ export class TestApp {
|
|||
this._app = {
|
||||
$realtime: this._realtime,
|
||||
$cookies: {
|
||||
set(name, id, value) {
|
||||
set(name, value) {
|
||||
cookieStorage[name] = value
|
||||
},
|
||||
get(name) {
|
||||
|
@ -134,6 +134,7 @@ export class TestApp {
|
|||
this._vueContext,
|
||||
extraPluginSetupFunc
|
||||
)
|
||||
this.store.$cookies = this._app.$cookies
|
||||
this._initialCleanStoreState = _.cloneDeep(this.store.state)
|
||||
Papa.arrayToString = (array) => {
|
||||
return Papa.unparse([array])
|
||||
|
|
|
@ -31,6 +31,22 @@ describe('Public View Page Tests', () => {
|
|||
expect(publicGridViewPage.element).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('Publicly shared view is not saved as a last visited view', async () => {
|
||||
const slug = 'testSlug'
|
||||
const gridViewName = 'my public grid view name'
|
||||
givenAPubliclySharedGridViewWithSlug(gridViewName, slug)
|
||||
|
||||
await testApp.mount(PublicGrid, {
|
||||
asyncDataParams: {
|
||||
slug,
|
||||
},
|
||||
})
|
||||
|
||||
const allCookies = testApp.store.$cookies
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBe(0)
|
||||
})
|
||||
|
||||
function givenAPubliclySharedGridViewWithSlug(name, slug) {
|
||||
const fields = [
|
||||
{
|
||||
|
|
464
web-frontend/test/unit/database/view.spec.js
Normal file
464
web-frontend/test/unit/database/view.spec.js
Normal file
|
@ -0,0 +1,464 @@
|
|||
import { TestApp } from '@baserow/test/helpers/testApp'
|
||||
import Table from '@baserow/modules/database/pages/table'
|
||||
import { utf8ByteSize } from '@baserow/modules/database/utils/view'
|
||||
|
||||
// Mock out debounce so we dont have to wait or simulate waiting for the various
|
||||
// debounces in the search functionality.
|
||||
jest.mock('lodash/debounce', () => jest.fn((fn) => fn))
|
||||
|
||||
describe('View Tests', () => {
|
||||
let testApp = null
|
||||
let mockServer = null
|
||||
|
||||
beforeAll(() => {
|
||||
testApp = new TestApp()
|
||||
mockServer = testApp.mockServer
|
||||
})
|
||||
|
||||
afterEach(() => testApp.afterEach())
|
||||
|
||||
test('Default view is being set correctly initially', async () => {
|
||||
const { application, table, views } =
|
||||
await givenATableInTheServerWithMultipleViews()
|
||||
|
||||
const gridView = views[0]
|
||||
const galleryView = views[1]
|
||||
|
||||
// The first view is the Grid view, the Default view is the Gallery view which
|
||||
// is going to be rendered initially:
|
||||
const tableComponent = await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: table.id,
|
||||
viewId: galleryView.id,
|
||||
},
|
||||
})
|
||||
|
||||
const allCookies = testApp.store.$cookies
|
||||
const tableId = gridView.table_id
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(gridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBe(1)
|
||||
const defaultViewIdObject = cookieValue.find(
|
||||
(obj) => obj.table_id === tableId
|
||||
)
|
||||
expect(defaultViewIdObject.table_id).toBe(tableId)
|
||||
expect(defaultViewIdObject.id).toBe(galleryView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(galleryView.id)
|
||||
// Check if component is rendered:
|
||||
expect(tableComponent.find('div.gallery-view').exists()).toBe(true)
|
||||
expect(tableComponent.find('div.grid-view').exists()).toBe(false)
|
||||
})
|
||||
|
||||
test('Default view is being set correctly after changing views', async () => {
|
||||
const { application, table, views } =
|
||||
await givenATableInTheServerWithMultipleViews()
|
||||
|
||||
const gridView = views[0]
|
||||
const galleryView = views[1]
|
||||
|
||||
// The first view is the Grid view, the Default view is the Gallery view which
|
||||
// is going to be rendered initially:
|
||||
const tableComponent = await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: table.id,
|
||||
viewId: galleryView.id,
|
||||
},
|
||||
})
|
||||
|
||||
const allCookies = testApp.store.$cookies
|
||||
const tableId = gridView.table_id
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(gridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBe(1)
|
||||
const defaultViewIdObject = cookieValue.find(
|
||||
(obj) => obj.table_id === tableId
|
||||
)
|
||||
expect(defaultViewIdObject.table_id).toBe(tableId)
|
||||
expect(defaultViewIdObject.id).toBe(galleryView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(galleryView.id)
|
||||
// Check if component is rendered:
|
||||
expect(tableComponent.find('div.gallery-view').exists()).toBe(true)
|
||||
expect(tableComponent.find('div.grid-view').exists()).toBe(false)
|
||||
|
||||
// Let's switch back (select) the Grid (first) view:
|
||||
testApp.store.dispatch('view/selectById', gridView.id)
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(gridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const updatedCookieValue = allCookies.get('defaultViewId')
|
||||
expect(updatedCookieValue.length).toBe(1)
|
||||
const updatedDefaultViewIdObject = updatedCookieValue.find(
|
||||
(obj) => obj.table_id === tableId
|
||||
)
|
||||
expect(updatedDefaultViewIdObject.table_id).toBe(tableId)
|
||||
expect(updatedDefaultViewIdObject.id).toBe(gridView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(gridView.id)
|
||||
})
|
||||
|
||||
test('Default view is being set correctly after switching tables', async () => {
|
||||
const { application, tables, views } =
|
||||
await givenATableInTheServerWithMultipleTables()
|
||||
|
||||
const firstTable = tables[0]
|
||||
const secondTable = tables[1]
|
||||
const firstTableGridView = views[0]
|
||||
const secondTableGridView = views[1]
|
||||
|
||||
// The first (and default) view is the Grid view, which is going to be rendered
|
||||
// initially for the firstTable:
|
||||
const firstTableComponent = await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: firstTable.id,
|
||||
},
|
||||
})
|
||||
|
||||
const allCookies = testApp.store.$cookies
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(firstTableGridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBe(1)
|
||||
const defaultViewIdObject = cookieValue.find(
|
||||
(obj) => obj.table_id === firstTableGridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObject.table_id).toBe(firstTableGridView.table_id)
|
||||
expect(defaultViewIdObject.id).toBe(firstTableGridView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(firstTableGridView.id)
|
||||
// Check if component is rendered:
|
||||
expect(firstTableComponent.find('div.grid-view').exists()).toBe(true)
|
||||
expect(firstTableComponent.find('div.gallery-view').exists()).toBe(false)
|
||||
|
||||
// The first (and default) view is the Grid view, which is going to be rendered
|
||||
// initially for the secondTable:
|
||||
await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: secondTable.id,
|
||||
},
|
||||
})
|
||||
|
||||
// Let's switch to a different table in the database:
|
||||
testApp.store.dispatch('table/selectById', {
|
||||
databaseId: application.id,
|
||||
tableId: secondTable.id,
|
||||
})
|
||||
testApp.store.dispatch('view/selectById', secondTableGridView.id)
|
||||
|
||||
const allCookiesAfterChangingTable = testApp.store.$cookies
|
||||
const cookieValueAfterChangingTable =
|
||||
allCookiesAfterChangingTable.get('defaultViewId')
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(secondTableGridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
expect(cookieValueAfterChangingTable.length).toBe(2)
|
||||
const defaultViewIdObjectAfterChangingTable =
|
||||
cookieValueAfterChangingTable.find(
|
||||
(obj) => obj.table_id === secondTableGridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObjectAfterChangingTable.table_id).toBe(
|
||||
secondTableGridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObjectAfterChangingTable.id).toBe(
|
||||
secondTableGridView.id
|
||||
)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(secondTableGridView.id)
|
||||
// Check if component is rendered:
|
||||
expect(firstTableComponent.find('div.grid-view').exists()).toBe(true)
|
||||
expect(firstTableComponent.find('div.gallery-view').exists()).toBe(false)
|
||||
|
||||
// Let's switch back to the first table in the database and see if first table's
|
||||
// default view is appended to the *end* of remembered views array:
|
||||
await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: firstTable.id,
|
||||
},
|
||||
})
|
||||
testApp.store.dispatch('table/selectById', {
|
||||
databaseId: application.id,
|
||||
tableId: firstTable.id,
|
||||
})
|
||||
testApp.store.dispatch('view/selectById', firstTableGridView.id)
|
||||
|
||||
const allCookiesAfterSwitchingBack = testApp.store.$cookies
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(firstTableGridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const cookieValueAfterSwitchingBack =
|
||||
allCookiesAfterSwitchingBack.get('defaultViewId')
|
||||
expect(cookieValueAfterSwitchingBack.length).toBe(2)
|
||||
const defaultViewIdObjectAfterSwitchingBack =
|
||||
cookieValueAfterSwitchingBack.find(
|
||||
(obj) => obj.table_id === firstTableGridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObjectAfterSwitchingBack.table_id).toBe(
|
||||
firstTableGridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObjectAfterSwitchingBack.id).toBe(firstTableGridView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(firstTableGridView.id)
|
||||
|
||||
expect(cookieValueAfterSwitchingBack[cookieValue.length - 1]).toMatchObject(
|
||||
defaultViewIdObjectAfterSwitchingBack
|
||||
)
|
||||
})
|
||||
|
||||
test('Default view is being set correctly initially only from cookie', async () => {
|
||||
// set the cookie, render table without view id passed in, this should render
|
||||
// the default (Gallery) view
|
||||
const { application, table, views } =
|
||||
await givenATableInTheServerWithMultipleViews()
|
||||
|
||||
const gridView = views[0]
|
||||
const galleryView = views[1]
|
||||
|
||||
const tableId = gridView.table_id
|
||||
const allCookies = testApp.store.$cookies
|
||||
|
||||
// Set the cookie for defaultView manually:
|
||||
const defaultViewIdData = []
|
||||
defaultViewIdData.push({
|
||||
table_id: galleryView.table_id,
|
||||
id: galleryView.id,
|
||||
})
|
||||
allCookies.set('defaultViewId', defaultViewIdData)
|
||||
|
||||
// The first view is the Grid view, the Default view is the Gallery view,
|
||||
// we're not rendering any view initially and Default view (Gallery view)
|
||||
// should be picked up from the cookie
|
||||
const tableComponent = await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: table.id,
|
||||
},
|
||||
})
|
||||
|
||||
// Check if Vuex store is updated correctly (first view):
|
||||
expect(testApp.store.getters['view/first'].id).toBe(gridView.id)
|
||||
// Check if cookie is updated correctly (default view):
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBe(1)
|
||||
const defaultViewIdObject = cookieValue.find(
|
||||
(obj) => obj.table_id === tableId
|
||||
)
|
||||
expect(defaultViewIdObject.table_id).toBe(tableId)
|
||||
expect(defaultViewIdObject.id).toBe(galleryView.id)
|
||||
// Check if Vuex store is updated correctly (default view):
|
||||
expect(testApp.store.getters['view/defaultId']).toBe(galleryView.id)
|
||||
// Check if component is rendered:
|
||||
expect(tableComponent.find('div.gallery-view').exists()).toBe(true)
|
||||
expect(tableComponent.find('div.grid-view').exists()).toBe(false)
|
||||
})
|
||||
|
||||
test('Changing default view updates cookies array correctly', async () => {
|
||||
const { application, table, views } =
|
||||
await givenATableInTheServerWithMultipleViews()
|
||||
|
||||
const gridView = views[0]
|
||||
|
||||
const allCookies = testApp.store.$cookies
|
||||
|
||||
// Calculate the size needed to exceed 4096 bytes
|
||||
const targetSize = 10000 // Choose a size greater than 4096
|
||||
|
||||
// Generate random data to fill up the cookie
|
||||
const randomData = []
|
||||
let totalSize = 0
|
||||
let i = 0
|
||||
while (totalSize < targetSize) {
|
||||
const randomTableId = i++
|
||||
const randomViewId = i++
|
||||
const entry = { table_id: randomTableId, id: randomViewId }
|
||||
const entrySize = utf8ByteSize(encodeURIComponent(JSON.stringify(entry)))
|
||||
if (totalSize + entrySize <= targetSize) {
|
||||
randomData.push(entry)
|
||||
totalSize += entrySize
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
allCookies.set('defaultViewId', randomData)
|
||||
const originalDataLength = randomData.length
|
||||
|
||||
// Mount the component, which should update the cookies
|
||||
await testApp.mount(Table, {
|
||||
asyncDataParams: {
|
||||
databaseId: application.id,
|
||||
tableId: table.id,
|
||||
viewId: gridView.id,
|
||||
},
|
||||
})
|
||||
|
||||
// The Default view is the Grid view and it should be set (appended) in the cookie
|
||||
const cookieValue = allCookies.get('defaultViewId')
|
||||
expect(cookieValue.length).toBeGreaterThan(0)
|
||||
|
||||
const defaultViewIdObject = cookieValue.find(
|
||||
(obj) => obj.table_id === gridView.table_id
|
||||
)
|
||||
expect(defaultViewIdObject.table_id).toBe(gridView.table_id)
|
||||
expect(defaultViewIdObject.id).toBe(gridView.id)
|
||||
|
||||
// Check if gridView is set as the last view in the array
|
||||
expect(cookieValue[cookieValue.length - 1]).toMatchObject(
|
||||
defaultViewIdObject
|
||||
)
|
||||
|
||||
// Ensure that the first element is removed from the cookie array
|
||||
const updatedCookieValue = allCookies.get('defaultViewId')
|
||||
expect(updatedCookieValue).not.toContainEqual(randomData[0])
|
||||
expect(updatedCookieValue.length).toBeLessThan(originalDataLength)
|
||||
})
|
||||
|
||||
async function givenATableInTheServerWithMultipleViews() {
|
||||
const table = mockServer.createTable()
|
||||
const { application } = await mockServer.createAppAndWorkspace(table)
|
||||
const gridView = mockServer.createGridView(application, table, {
|
||||
viewId: 1,
|
||||
})
|
||||
const galleryView = mockServer.createGalleryView(application, table, {
|
||||
viewId: 2,
|
||||
})
|
||||
|
||||
mockServer.mock
|
||||
.onGet(`/database/views/table/${table.id}/`)
|
||||
.reply(200, [gridView, galleryView])
|
||||
|
||||
const fields = mockServer.createFields(application, table, [
|
||||
{
|
||||
name: 'Name',
|
||||
type: 'text',
|
||||
primary: true,
|
||||
},
|
||||
{
|
||||
name: 'Last name',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'Notes',
|
||||
type: 'long_text',
|
||||
},
|
||||
{
|
||||
name: 'Active',
|
||||
type: 'boolean',
|
||||
},
|
||||
])
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 1,
|
||||
order: 0,
|
||||
field_1: 'name',
|
||||
field_2: 'last_name',
|
||||
field_3: 'notes',
|
||||
field_4: false,
|
||||
},
|
||||
]
|
||||
|
||||
mockServer.createGridRows(gridView, fields, rows)
|
||||
mockServer.createFields(application, table, fields)
|
||||
mockServer.createGalleryRows(galleryView, fields, rows)
|
||||
|
||||
const views = []
|
||||
views.push(gridView)
|
||||
views.push(galleryView)
|
||||
return { application, table, views }
|
||||
}
|
||||
|
||||
async function givenATableInTheServerWithMultipleTables() {
|
||||
const firstTable = mockServer.createTable(1, 'Test Table 1')
|
||||
const secondTable = mockServer.createTable(2, 'Test Table 2')
|
||||
const { application } =
|
||||
await mockServer.createAppAndWorkspaceWithMultipleTables([
|
||||
firstTable,
|
||||
secondTable,
|
||||
])
|
||||
|
||||
// First table - 1 view:
|
||||
const firstTableGridView = mockServer.createGridView(
|
||||
application,
|
||||
firstTable,
|
||||
{
|
||||
viewId: 1,
|
||||
}
|
||||
)
|
||||
mockServer.mock
|
||||
.onGet(`/database/views/table/${firstTable.id}/`)
|
||||
.reply(200, [firstTableGridView])
|
||||
|
||||
// Second table - 1 view:
|
||||
const secondTableGridView = mockServer.createGridView(
|
||||
application,
|
||||
secondTable,
|
||||
{
|
||||
viewId: 2,
|
||||
}
|
||||
)
|
||||
mockServer.mock
|
||||
.onGet(`/database/views/table/${secondTable.id}/`)
|
||||
.reply(200, [secondTableGridView])
|
||||
|
||||
const fields = mockServer.createFields(application, firstTable, [
|
||||
{
|
||||
name: 'Name',
|
||||
type: 'text',
|
||||
primary: true,
|
||||
},
|
||||
{
|
||||
name: 'Last name',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'Notes',
|
||||
type: 'long_text',
|
||||
},
|
||||
{
|
||||
name: 'Active',
|
||||
type: 'boolean',
|
||||
},
|
||||
])
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 1,
|
||||
order: 0,
|
||||
field_1: 'name',
|
||||
field_2: 'last_name',
|
||||
field_3: 'notes',
|
||||
field_4: false,
|
||||
},
|
||||
]
|
||||
|
||||
mockServer.createGridRows(firstTableGridView, fields, rows)
|
||||
mockServer.createGridRows(secondTableGridView, fields, rows)
|
||||
mockServer.createFields(application, firstTable, fields)
|
||||
mockServer.createFields(application, secondTable, fields)
|
||||
|
||||
const views = []
|
||||
views.push(firstTableGridView)
|
||||
views.push(secondTableGridView)
|
||||
|
||||
const tables = []
|
||||
tables.push(firstTable)
|
||||
tables.push(secondTable)
|
||||
return { application, tables, views }
|
||||
}
|
||||
})
|
Loading…
Add table
Reference in a new issue