From 4b1d6ee752c23fd1d8c0d88d59285322ba9d7ac4 Mon Sep 17 00:00:00 2001 From: Sergey Vinogradov Date: Fri, 29 Sep 2023 15:27:53 +0300 Subject: [PATCH] test: add unit tests for DataProviderController (#6528) --- .../data-provider-controller.js | 7 +- .../data-provider-controller-cache.test.js | 248 +++++++++++++++ ...a-provider-controller-data-loading.test.js | 298 ++++++++++++++++++ .../data-provider-controller-events.test.js | 104 ++++++ .../test/data-provider-controller.test.js | 226 +++++++++++++ 5 files changed, 881 insertions(+), 2 deletions(-) create mode 100644 packages/component-base/test/data-provider-controller-cache.test.js create mode 100644 packages/component-base/test/data-provider-controller-data-loading.test.js create mode 100644 packages/component-base/test/data-provider-controller-events.test.js create mode 100644 packages/component-base/test/data-provider-controller.test.js diff --git a/packages/component-base/src/data-provider-controller/data-provider-controller.js b/packages/component-base/src/data-provider-controller/data-provider-controller.js index b9d25948b0..779e0df2fe 100644 --- a/packages/component-base/src/data-provider-controller/data-provider-controller.js +++ b/packages/component-base/src/data-provider-controller/data-provider-controller.js @@ -211,13 +211,16 @@ export class DataProviderController extends EventTarget { return; } - const params = { + let params = { page, pageSize: this.pageSize, parentItem: cache.parentItem, - ...this.dataProviderParams(), }; + if (this.dataProviderParams) { + params = { ...params, ...this.dataProviderParams() }; + } + const callback = (items, size) => { if (size !== undefined) { cache.size = size; diff --git a/packages/component-base/test/data-provider-controller-cache.test.js b/packages/component-base/test/data-provider-controller-cache.test.js new file mode 100644 index 0000000000..daec383f49 --- /dev/null +++ b/packages/component-base/test/data-provider-controller-cache.test.js @@ -0,0 +1,248 @@ +import { expect } from '@esm-bundle/chai'; +import { Cache } from '../src/data-provider-controller/cache.js'; + +describe('DataProviderController - Cache', () => { + let cache; + let expandedItems = []; + + function isExpanded(item) { + return expandedItems.includes(item); + } + + describe('default', () => { + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + }); + + it('should have size', () => { + expect(cache.size).to.equal(500); + }); + + it('should have pageSize', () => { + expect(cache.pageSize).to.equal(50); + }); + + it('should have flatSize', () => { + expect(cache.flatSize).to.equal(500); + }); + + it('should have empty items', () => { + expect(cache.items).to.have.lengthOf(0); + }); + + it('should have empty subCaches', () => { + expect(cache.subCaches).to.have.lengthOf(0); + }); + + it('should not have parentCache', () => { + expect(cache.parentCache).to.be.undefined; + }); + + it('should not have parentCacheIndex', () => { + expect(cache.parentCacheIndex).to.be.undefined; + }); + + it('should not have parentItem', () => { + expect(cache.parentItem).to.be.undefined; + }); + + it('should not be in loading state', () => { + expect(cache.isLoading).to.be.false; + }); + }); + + describe('setPage', () => { + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + }); + + it('should insert the items at the correct position for page 0', () => { + cache.setPage(0, ['Item 0', 'Item 1']); + expect(cache.items).to.have.lengthOf(2); + expect(cache.items[0]).to.equal('Item 0'); + expect(cache.items[1]).to.equal('Item 1'); + }); + + it('should insert the items at the correct position for page 1', () => { + cache.setPage(1, ['Item 0', 'Item 1']); + expect(cache.items).to.have.lengthOf(52); + expect(cache.items[50]).to.equal('Item 0'); + expect(cache.items[51]).to.equal('Item 1'); + }); + }); + + describe('createSubCache', () => { + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + cache.setPage(0, ['Item 0']); + }); + + it('should create a sub-cache for the given item index', () => { + const subCache = cache.createSubCache(0); + expect(subCache.size).to.equal(0); + expect(subCache.pageSize).to.equal(50); + expect(subCache.flatSize).to.equal(0); + expect(subCache.parentItem).to.equal('Item 0'); + expect(subCache.parentCache).to.equal(cache); + expect(subCache.parentCacheIndex).to.equal(0); + }); + }); + + describe('subCaches', () => { + let subCache0, subCache1; + + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + // Add a sub-cache for an item at the index 20. + subCache0 = cache.createSubCache(20); + // Add a sub-cache for an item at the index 10. + subCache1 = cache.createSubCache(10); + }); + + it('should return sub-caches in the order of their associated items', () => { + expect(cache.subCaches).to.have.lengthOf(2); + expect(cache.subCaches[0]).to.equal(subCache1); + expect(cache.subCaches[1]).to.equal(subCache0); + }); + }); + + describe('removeSubCache', () => { + let subCache0, subCache1; + + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + subCache0 = cache.createSubCache(0); + subCache1 = cache.createSubCache(1); + }); + + it('should remove the sub-cache for the given item index', () => { + cache.removeSubCache(0); + expect(cache.subCaches).to.have.lengthOf(1); + expect(cache.subCaches[0]).to.equal(subCache1); + }); + }); + + describe('removeSubCaches', () => { + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + cache.createSubCache(0); + cache.createSubCache(1); + }); + + it('should remove all the sub-caches', () => { + cache.removeSubCaches(); + expect(cache.subCaches).to.have.lengthOf(0); + }); + }); + + describe('getSubCache', () => { + let subCache; + + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + subCache = cache.createSubCache(0); + }); + + it('should return the sub-cache for the given item index', () => { + expect(cache.getSubCache(0)).to.equal(subCache); + }); + + it('should return undefined if the sub-cache does not exist', () => { + expect(cache.getSubCache(1)).to.be.undefined; + }); + + it('should return undefined if the sub-cache has been removed', () => { + cache.removeSubCache(0); + expect(cache.getSubCache(0)).to.be.undefined; + }); + }); + + describe('isLoading', () => { + let subCache; + + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + subCache = cache.createSubCache(0); + }); + + it('should return true when the cache has pending requests', () => { + cache.pendingRequests[0] = (_items, _size) => {}; + expect(cache.isLoading).to.be.true; + }); + + it('should return true when one of the sub-caches has pending requests', () => { + subCache.pendingRequests[0] = (_items, _size) => {}; + expect(cache.isLoading).to.be.true; + }); + + it('should return true if not all the pending requests has been resolved', () => { + cache.pendingRequests[0] = (_items, _size) => {}; + subCache.pendingRequests[0] = (_items, _size) => {}; + delete cache.pendingRequests[0]; + expect(cache.isLoading).to.be.true; + }); + + it('should return false after all the pending requests have been resolved', () => { + cache.pendingRequests[0] = (_items, _size) => {}; + subCache.pendingRequests[0] = (_items, _size) => {}; + delete cache.pendingRequests[0]; + delete subCache.pendingRequests[0]; + expect(cache.isLoading).to.be.false; + }); + + it('should return false if the sub-cache with pending requests has been removed', () => { + subCache.pendingRequests[0] = (_items, _size) => {}; + cache.removeSubCache(0); + expect(cache.isLoading).to.be.false; + }); + }); + + describe('flatSize', () => { + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + cache.setPage(0, ['Item 0', 'Item 1']); + const subCache0 = cache.createSubCache(0); + subCache0.size = 50; + const subCache1 = cache.createSubCache(1); + subCache1.size = 100; + subCache1.setPage(0, ['Item 1-0']); + const subCache1SubCache0 = subCache1.createSubCache(0); + subCache1SubCache0.size = 200; + }); + + it('should include expanded sub-caches after recalculation', () => { + expandedItems = ['Item 0', 'Item 1', 'Item 1-0']; + cache.recalculateFlatSize(); + expect(cache.flatSize).to.equal(850); + }); + + it('should exclude collapsed sub-caches after recalculation', () => { + expandedItems = ['Item 0']; + cache.recalculateFlatSize(); + expect(cache.flatSize).to.equal(550); + }); + }); + + describe('getFlatIndex', () => { + let subCache0, subCache1; + + beforeEach(() => { + cache = new Cache({ isExpanded }, 50, 500); + cache.setPage(0, ['Item 0', 'Item 1', 'Item 2']); + subCache0 = cache.createSubCache(0); + subCache0.size = 100; + subCache1 = cache.createSubCache(1); + subCache1.size = 200; + expandedItems = ['Item 0']; + cache.recalculateFlatSize(); + }); + + it('should include expanded sub-caches that precede the given index', () => { + expect(cache.getFlatIndex(1)).to.equal(101); + }); + + it('should exclude collapsed sub-caches that precede the given index', () => { + expect(cache.getFlatIndex(2)).to.equal(102); + }); + }); +}); diff --git a/packages/component-base/test/data-provider-controller-data-loading.test.js b/packages/component-base/test/data-provider-controller-data-loading.test.js new file mode 100644 index 0000000000..3b7fe38e04 --- /dev/null +++ b/packages/component-base/test/data-provider-controller-data-loading.test.js @@ -0,0 +1,298 @@ +import { expect } from '@esm-bundle/chai'; +import { aTimeout } from '@vaadin/testing-helpers'; +import sinon from 'sinon'; +import '@vaadin/testing-helpers'; +import { DataProviderController } from '../src/data-provider-controller/data-provider-controller.js'; + +function createDataProvider({ size }) { + return ({ page, pageSize, parentItem }, callback) => { + const items = Array.from({ length: size }, (_, i) => `${parentItem ?? 'Item'}-${i}`); + const startIndex = page * pageSize; + const pageItems = items.splice(startIndex, pageSize); + setTimeout(() => callback(pageItems, size)); + }; +} + +describe('DataProviderController - data loading', () => { + let host, controller, dataProviderSpy; + let expandedItems = []; + + function isExpanded(item) { + return expandedItems.includes(item); + } + + beforeEach(() => { + host = document.createElement('div'); + }); + + describe('loadFirstPage', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 2, + dataProvider: createDataProvider({ size: 10 }), + }); + + dataProviderSpy = sinon.spy(controller, 'dataProvider'); + }); + + it('should request first page', () => { + controller.loadFirstPage(); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: undefined }); + expect(dataProviderSpy.args[0][1]).to.be.a('function'); + }); + + it('should set loading state to true when page is requested', () => { + controller.loadFirstPage(); + expect(controller.isLoading()).to.be.true; + }); + + it('should add items to the cache when page is received', async () => { + controller.loadFirstPage(); + await aTimeout(0); + expect(controller.rootCache.items).to.have.lengthOf(2); + expect(controller.rootCache.items).to.eql(['Item-0', 'Item-1']); + }); + + it('should update size for the cache when page is received', async () => { + controller.loadFirstPage(); + await aTimeout(0); + expect(controller.rootCache.size).to.equal(10); + }); + + it('should recalculate flatSize when page is received', async () => { + controller.loadFirstPage(); + await aTimeout(0); + expect(controller.flatSize).to.equal(10); + }); + + it('should set loading state to false when page is received', async () => { + controller.loadFirstPage(); + await aTimeout(0); + expect(controller.isLoading()).to.be.false; + }); + + it('should not request first page if it is already loading', () => { + controller.loadFirstPage(); + controller.loadFirstPage(); + expect(dataProviderSpy).to.be.calledOnce; + }); + + it('should request first page again even if it is already loaded', async () => { + controller.loadFirstPage(); + await aTimeout(0); + dataProviderSpy.resetHistory(); + controller.loadFirstPage(); + expect(dataProviderSpy).to.be.calledOnce; + }); + }); + + describe('ensureFlatIndexLoaded', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 2, + isExpanded, + dataProvider: createDataProvider({ size: 10 }), + }); + + dataProviderSpy = sinon.spy(controller, 'dataProvider'); + }); + + it('should request page 0 when called with index 0', () => { + controller.ensureFlatIndexLoaded(0); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: undefined }); + }); + + it('should request page 0 when called with index 1', () => { + controller.ensureFlatIndexLoaded(1); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: undefined }); + }); + + it('should request page 1 when called with index 2', () => { + controller.ensureFlatIndexLoaded(2); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 1, pageSize: 2, parentItem: undefined }); + }); + + it('should not request page if it is already loading', () => { + controller.ensureFlatIndexLoaded(0); + controller.ensureFlatIndexLoaded(1); + controller.ensureFlatIndexLoaded(0); + controller.ensureFlatIndexLoaded(1); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: undefined }); + }); + + it('should not request page if the corresponding item is already loaded', async () => { + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + dataProviderSpy.resetHistory(); + controller.ensureFlatIndexLoaded(0); + expect(dataProviderSpy).to.be.not.called; + }); + + it('should add items to the root cache when page is received', async () => { + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + expect(controller.rootCache.items).eql(['Item-0', 'Item-1']); + }); + + it('should update size for the root cache when page is received', async () => { + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + expect(controller.rootCache.size).to.equal(10); + }); + }); + + describe('ensureFlatIndexHierarchy', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 2, + isExpanded, + dataProvider: createDataProvider({ size: 10 }), + }); + + dataProviderSpy = sinon.spy(controller, 'dataProvider'); + }); + + describe('index is not loaded', () => { + it('should not create a sub-cache', () => { + controller.ensureFlatIndexHierarchy(0); + expect(controller.rootCache.subCaches).to.have.lengthOf(0); + }); + + it('should not request data', () => { + controller.ensureFlatIndexHierarchy(0); + expect(dataProviderSpy).to.be.not.called; + }); + }); + + describe('index is loaded but not expanded', () => { + beforeEach(async () => { + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + dataProviderSpy.resetHistory(); + }); + + it('should not create a sub-cache', () => { + controller.ensureFlatIndexHierarchy(0); + expect(controller.rootCache.subCaches).to.have.lengthOf(0); + }); + + it('should not request data', () => { + controller.ensureFlatIndexHierarchy(0); + expect(dataProviderSpy).to.be.not.called; + }); + }); + + describe('index is loaded and expanded', () => { + beforeEach(async () => { + expandedItems = ['Item-0']; + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + dataProviderSpy.resetHistory(); + }); + + it('should create a sub-cache', () => { + controller.ensureFlatIndexHierarchy(0); + const subCache = controller.rootCache.getSubCache(0); + expect(subCache).to.exist; + expect(subCache.parentItem).to.equal('Item-0'); + expect(subCache.parentCache).to.equal(controller.rootCache); + }); + + it('should request first page for the sub-cache', () => { + controller.ensureFlatIndexHierarchy(0); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: 'Item-0' }); + }); + + it('should add items to the sub-cache when first page is received', async () => { + controller.ensureFlatIndexHierarchy(0); + await aTimeout(0); + const subCache = controller.rootCache.getSubCache(0); + expect(subCache.items).to.have.lengthOf(2); + expect(subCache.items).to.eql(['Item-0-0', 'Item-0-1']); + }); + + it('should update size for the sub-cache when first page is received', async () => { + controller.ensureFlatIndexHierarchy(0); + await aTimeout(0); + const subCache = controller.rootCache.getSubCache(0); + expect(subCache.size).to.equal(10); + }); + }); + + describe('ensureFlatIndexLoaded', () => { + /** + * 0: Item-0 + * 1: Item-0-0 + * 2: Item-0-1 + * 3: not loaded + * ... + * 11: Item-1 + */ + beforeEach(async () => { + expandedItems = ['Item-0']; + controller.ensureFlatIndexLoaded(0); + await aTimeout(0); + controller.ensureFlatIndexHierarchy(0); + await aTimeout(0); + dataProviderSpy.resetHistory(); + }); + + it('should not request data when called with indexes 0, 1, 2', () => { + controller.ensureFlatIndexLoaded(0); + controller.ensureFlatIndexLoaded(1); + controller.ensureFlatIndexLoaded(2); + expect(dataProviderSpy).to.be.not.called; + }); + + it('should request page 1 for the sub-level when called with index 3', () => { + controller.ensureFlatIndexLoaded(3); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 1, pageSize: 2, parentItem: 'Item-0' }); + }); + + it('should set loading state to true when page is requested', () => { + controller.ensureFlatIndexLoaded(3); + expect(controller.isLoading()).to.be.true; + }); + + it('should add items to the sub-cache when page is received', async () => { + controller.ensureFlatIndexLoaded(3); + await aTimeout(0); + const subCache = controller.rootCache.getSubCache(0); + expect(subCache.items).to.have.lengthOf(4); + expect(subCache.items).to.eql(['Item-0-0', 'Item-0-1', 'Item-0-2', 'Item-0-3']); + }); + + it('should set loading state to false when page is received', async () => { + controller.ensureFlatIndexLoaded(3); + await aTimeout(0); + expect(controller.isLoading()).to.be.false; + }); + }); + }); + + describe('dataProviderParams', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 2, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + dataProviderParams: () => ({ filter: 'bar' }), + }); + + dataProviderSpy = sinon.spy(controller, 'dataProvider'); + }); + + it('should pass dataProviderParams to dataProvider', () => { + controller.loadFirstPage(); + expect(dataProviderSpy).to.be.calledOnce; + expect(dataProviderSpy.args[0][0]).to.eql({ page: 0, pageSize: 2, parentItem: undefined, filter: 'bar' }); + }); + }); +}); diff --git a/packages/component-base/test/data-provider-controller-events.test.js b/packages/component-base/test/data-provider-controller-events.test.js new file mode 100644 index 0000000000..75401993e6 --- /dev/null +++ b/packages/component-base/test/data-provider-controller-events.test.js @@ -0,0 +1,104 @@ +import { expect } from '@esm-bundle/chai'; +import { aTimeout } from '@vaadin/testing-helpers'; +import sinon from 'sinon'; +import { DataProviderController } from '../src/data-provider-controller/data-provider-controller.js'; + +function createDataProvider({ size }) { + const items = Array.from({ length: size }, (_, i) => `Item ${i}`); + + return ({ page, pageSize }, callback) => { + const startIndex = page * pageSize; + const pageItems = items.splice(startIndex, pageSize); + setTimeout(() => callback(pageItems, size)); + }; +} + +describe('DataProviderController - events', () => { + let host, controller, pageRequestedSpy, pageReceivedSpy, pageLoadedSpy; + + beforeEach(() => { + host = document.createElement('div'); + + controller = new DataProviderController(host, { + pageSize: 2, + dataProvider: createDataProvider({ size: 10 }), + }); + + pageRequestedSpy = sinon.spy(); + controller.addEventListener('page-requested', pageRequestedSpy); + pageReceivedSpy = sinon.spy(); + controller.addEventListener('page-received', pageReceivedSpy); + pageLoadedSpy = sinon.spy(); + controller.addEventListener('page-loaded', pageLoadedSpy); + }); + + it('should fire page-requested event when a page request is made', () => { + controller.loadFirstPage(); + expect(pageRequestedSpy).to.be.calledOnce; + }); + + it('should fire page-received event when page items are received', async () => { + controller.loadFirstPage(); + expect(pageReceivedSpy).to.be.not.called; + await aTimeout(0); + expect(pageReceivedSpy).to.be.calledOnce; + }); + + it('should fire page-loaded event when a page request is completed', async () => { + controller.loadFirstPage(); + expect(pageLoadedSpy).to.be.not.called; + await aTimeout(0); + expect(pageLoadedSpy).to.be.calledOnce; + }); + + it('should fire page-loaded event after page-received event', async () => { + controller.loadFirstPage(); + await aTimeout(0); + expect(pageLoadedSpy).to.be.calledAfter(pageReceivedSpy); + }); + + it('should be in loading state when page-requested event is fired', () => { + controller.addEventListener('page-requested', () => { + expect(controller.isLoading()).to.be.true; + }); + + controller.loadFirstPage(); + }); + + it('should have flatSize recalculated when page-received event is fired', async () => { + controller.addEventListener('page-received', () => { + expect(controller.flatSize).to.equal(10); + }); + + controller.loadFirstPage(); + await aTimeout(0); + }); + + it('should have page items added when page-received event is fired', async () => { + controller.addEventListener('page-received', () => { + expect(controller.rootCache.items).to.have.lengthOf(2); + expect(controller.rootCache.items).to.eql(['Item 0', 'Item 1']); + }); + + controller.loadFirstPage(); + await aTimeout(0); + }); + + it('should be in loading state when page-received event is fired', async () => { + controller.addEventListener('page-received', () => { + expect(controller.isLoading()).to.be.true; + }); + + controller.loadFirstPage(); + await aTimeout(0); + }); + + it('should not be in loading state when page-loaded is fired', async () => { + controller.addEventListener('page-loaded', () => { + expect(controller.isLoading()).to.be.false; + }); + + controller.loadFirstPage(); + await aTimeout(0); + }); +}); diff --git a/packages/component-base/test/data-provider-controller.test.js b/packages/component-base/test/data-provider-controller.test.js new file mode 100644 index 0000000000..a37d4f7e4b --- /dev/null +++ b/packages/component-base/test/data-provider-controller.test.js @@ -0,0 +1,226 @@ +import { expect } from '@esm-bundle/chai'; +import sinon from 'sinon'; +import { Cache } from '../src/data-provider-controller/cache.js'; +import { DataProviderController } from '../src/data-provider-controller/data-provider-controller.js'; + +describe('DataProviderController', () => { + let host, controller; + + const expandedItems = []; + + function isExpanded(item) { + return expandedItems.includes(item); + } + + beforeEach(() => { + host = document.createElement('div'); + }); + + describe('default', () => { + let dataProvider; + + beforeEach(() => { + dataProvider = (_params, callback) => callback([], 0); + + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider, + }); + }); + + it('should not have size', () => { + expect(controller.size).to.be.undefined; + }); + + it('should have pageSize', () => { + expect(controller.pageSize).to.equal(50); + }); + + it('should have dataProvider', () => { + expect(controller.dataProvider).to.equal(dataProvider); + }); + + it('should have flatSize', () => { + expect(controller.flatSize).to.equal(0); + }); + + it('should have rootCache', () => { + expect(controller.rootCache).to.be.instanceOf(Cache); + }); + + it('should have rootCache size', () => { + expect(controller.rootCache.size).to.equal(0); + }); + + it('should have rootCache pageSize', () => { + expect(controller.rootCache.pageSize).to.equal(50); + }); + + it('should not have rootCache parentCache', () => { + expect(controller.rootCache.parentCache).to.be.undefined; + }); + + it('should not have rootCache parentCacheIndex', () => { + expect(controller.rootCache.parentCacheIndex).to.be.undefined; + }); + + it('should not be in loading state', () => { + expect(controller.isLoading()).to.be.false; + }); + }); + + describe('with size', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + size: 500, + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should have size', () => { + expect(controller.size).to.equal(500); + }); + + it('should have rootCache size', () => { + expect(controller.rootCache.size).to.equal(500); + }); + }); + + describe('with dataProviderParams', () => { + let dataProviderParams; + + beforeEach(() => { + dataProviderParams = () => ({ filter: 'bar' }); + + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + dataProviderParams, + }); + }); + + it('should have dataProviderParams', () => { + expect(controller.dataProviderParams).to.equal(dataProviderParams); + }); + }); + + describe('clearCache', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + size: 500, + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should create a new cache', () => { + const { rootCache } = controller; + controller.clearCache(); + expect(controller.rootCache).to.not.equal(rootCache); + }); + + it('should set size for the new cache', () => { + controller.clearCache(); + expect(controller.rootCache.size).to.equal(500); + }); + + it('should set pageSize for the new cache', () => { + controller.clearCache(); + expect(controller.rootCache.pageSize).to.equal(50); + }); + }); + + describe('setSize', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should update size', () => { + controller.setSize(100); + expect(controller.size).to.equal(100); + }); + + it('should update rootCache size', () => { + controller.setSize(100); + expect(controller.rootCache.size).to.equal(100); + }); + + it('should recalculate flatSize', () => { + controller.setSize(100); + expect(controller.flatSize).to.equal(100); + }); + }); + + describe('setPageSize', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should update pageSize', () => { + controller.setPageSize(10); + expect(controller.pageSize).to.equal(10); + }); + + it('should update rootCache pageSize', () => { + controller.setPageSize(10); + expect(controller.rootCache.pageSize).to.equal(10); + }); + + it('should clear the cache', () => { + const spy = sinon.spy(controller, 'clearCache'); + controller.setPageSize(10); + expect(spy.calledOnce).to.be.true; + }); + }); + + describe('setDataProvider', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should update dataProvider', () => { + const dataProvider = (_params, callback) => callback([], 0); + controller.setDataProvider(dataProvider); + expect(controller.dataProvider).to.equal(dataProvider); + }); + + it('should clear the cache', () => { + const spy = sinon.spy(controller, 'clearCache'); + controller.setDataProvider((_params, callback) => callback([], 0)); + expect(spy.calledOnce).to.be.true; + }); + }); + + describe('recalculateFlatSize', () => { + beforeEach(() => { + controller = new DataProviderController(host, { + pageSize: 50, + isExpanded, + dataProvider: (_params, callback) => callback([], 0), + }); + }); + + it('should delegate the call to rootCache', () => { + const spy = sinon.spy(controller.rootCache, 'recalculateFlatSize'); + controller.recalculateFlatSize(); + expect(spy.calledOnce).to.be.true; + }); + }); +});