import { TestApp } from '@baserow/test/helpers/testApp' import Table from '@baserow/modules/database/pages/table' import { DEFAULT_VIEW_ID_COOKIE_NAME, readDefaultViewIdFromCookie, decodeDefaultViewIdPerTable, encodeDefaultViewIdPerTable, } from '@baserow/modules/database/utils/view' // Mock out debounce so we don't 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 let originalReplaceFunc = null beforeAll(() => { testApp = new TestApp() mockServer = testApp.mockServer // Mock the redirect function so we can test the component without having to // worry about the router. originalReplaceFunc = testApp._app.$router.replace testApp._app.$router.replace = ({ params }) => { return Table.asyncData({ app: testApp._app, store: testApp._app.store, params, }) } }) afterEach(() => testApp.afterEach()) afterAll(() => { testApp._app.$router.replace = originalReplaceFunc }) test('Default view is being set correctly initially', async () => { const allCookies = testApp.store.$cookies 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 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 defaultViewId = readDefaultViewIdFromCookie(allCookies, tableId) expect(defaultViewId).not.toBe(null) const defaultView = testApp.store.getters['view/get'](defaultViewId) expect(defaultView.table_id).toBe(tableId) expect(defaultView.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 defaultViewId = readDefaultViewIdFromCookie(allCookies, tableId) expect(defaultViewId).not.toBe(null) const defaultView = testApp.store.getters['view/get'](defaultViewId) expect(defaultView.table_id).toBe(tableId) expect(defaultView.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 = decodeDefaultViewIdPerTable( allCookies.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) expect(updatedCookieValue.length).toBe(1) const updatedDefaultViewId = readDefaultViewIdFromCookie( allCookies, tableId ) const updatedDefaultView = testApp.store.getters['view/get'](updatedDefaultViewId) expect(updatedDefaultView.table_id).toBe(tableId) expect(updatedDefaultView.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 = decodeDefaultViewIdPerTable( allCookies.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) expect(cookieValue.length).toBe(1) const defaultViewId = readDefaultViewIdFromCookie( allCookies, firstTableGridView.table_id ) expect(defaultViewId).not.toBe(null) const defaultView = testApp.store.getters['view/get'](defaultViewId) expect(defaultView.table_id).toBe(firstTableGridView.table_id) expect(defaultView.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 = decodeDefaultViewIdPerTable( allCookiesAfterChangingTable.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) // 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 defaultViewIdAfterChangingTable = readDefaultViewIdFromCookie( allCookies, secondTableGridView.table_id ) expect(defaultViewIdAfterChangingTable).not.toBe(null) const defaultViewAfterChangingTable = testApp.store.getters['view/get']( defaultViewIdAfterChangingTable ) expect(defaultViewAfterChangingTable.table_id).toBe( secondTableGridView.table_id ) expect(defaultViewAfterChangingTable.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 = decodeDefaultViewIdPerTable( allCookiesAfterSwitchingBack.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) expect(cookieValueAfterSwitchingBack.length).toBe(2) const defaultViewIdAfterSwitchingBack = readDefaultViewIdFromCookie( allCookiesAfterSwitchingBack, firstTableGridView.table_id ) expect(defaultViewIdAfterSwitchingBack).not.toBe(null) const defaultViewAfterSwitchingBack = testApp.store.getters['view/get']( defaultViewIdAfterSwitchingBack ) expect(defaultViewAfterSwitchingBack.table_id).toBe( firstTableGridView.table_id ) expect(defaultViewAfterSwitchingBack.id).toBe(firstTableGridView.id) // Check if Vuex store is updated correctly (default view): expect(testApp.store.getters['view/defaultId']).toBe(firstTableGridView.id) }) test('Default view is being set correctly 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({ tableId: galleryView.table_id, viewId: galleryView.id, }) allCookies.set( DEFAULT_VIEW_ID_COOKIE_NAME, encodeDefaultViewIdPerTable(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 = decodeDefaultViewIdPerTable( allCookies.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) expect(cookieValue.length).toBe(1) const defaultViewId = readDefaultViewIdFromCookie(allCookies, tableId) expect(defaultViewId).not.toBe(null) const defaultView = testApp.store.getters['view/get'](defaultViewId) expect(defaultView.table_id).toBe(tableId) expect(defaultView.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] // Generate random data to fill up the cookie // Our cookie has a limit of 2kb, so we need to generate enough data to fill it up // For sure one entry will need more than 1 byte, so we can't just generate 2048 // entries const targetSize = 2048 const randomData = [] for (let i = 0; i < targetSize; i++) { const randomTableId = i const randomViewId = i const entry = { tableId: randomTableId, viewId: randomViewId } randomData.push(entry) } const allCookies = testApp.store.$cookies allCookies.set( DEFAULT_VIEW_ID_COOKIE_NAME, encodeDefaultViewIdPerTable(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 = decodeDefaultViewIdPerTable( allCookies.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) expect(cookieValue.length).toBeGreaterThan(0) const defaultViewIdObject = cookieValue[cookieValue.length - 1] expect(defaultViewIdObject.tableId).toBe(gridView.table_id) expect(defaultViewIdObject.viewId).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 = decodeDefaultViewIdPerTable( allCookies.get(DEFAULT_VIEW_ID_COOKIE_NAME) ) 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 } } })