diff --git a/src/plugins/search/search.js b/src/plugins/search/search.js index a30f67b74..b2164010d 100644 --- a/src/plugins/search/search.js +++ b/src/plugins/search/search.js @@ -20,7 +20,7 @@ db.version(1).stores({ expires: 'key, value', }); -async function saveData(maxAge, expireKey) { +async function saveData(maxAge, expireKey, indexKey) { const records = []; Object.values(INDEXES).forEach(entry => { @@ -43,10 +43,15 @@ async function saveData(maxAge, expireKey) { }); INDEXES = records; - await /** @type {any} */ (db).search.bulkPut(records); - await /** @type {any} */ (db).expires.put({ - key: expireKey, - value: Date.now() + maxAge, + const searchTable = db.table('search'); + const expiresTable = db.table('expires'); + await db.transaction('rw', searchTable, expiresTable, async () => { + await searchTable.where({ indexKey }).delete(); + await searchTable.bulkPut(records); + await expiresTable.put({ + key: expireKey, + value: Date.now() + maxAge, + }); }); } @@ -448,7 +453,7 @@ export async function init(config, vm) { const markComplete = async () => { if (len === ++count) { - await saveData(config.maxAge, expireKey); + await saveData(config.maxAge, expireKey, indexKey); } }; diff --git a/test/e2e/search.test.js b/test/e2e/search.test.js index cfcfdf99c..8020dd3d3 100644 --- a/test/e2e/search.test.js +++ b/test/e2e/search.test.js @@ -340,6 +340,67 @@ test.describe('Search Plugin Tests', () => { await expect(resultsHeadingElm).toHaveText('EmptyContent'); }); + test('does not restore deleted sections after rebuilding an expired cache', async ({ + page, + }) => { + const options = { + config: { + search: { paths: ['/'], namespace: 'cache-refresh', maxAge: -1 }, + }, + markdown: { homepage: '# Obsolete\n\nretiredkeyword' }, + scriptURLs: ['/dist/plugins/search.js'], + }; + const searchField = page.locator('input[type=search]'); + const results = page.locator('.results-panel .title'); + + await docsifyInit(options); + await searchField.fill('retiredkeyword'); + await expect(results).toHaveText('Obsolete'); + + options.config.search.maxAge = 60000; + options.markdown.homepage = '# Current\n\ncurrentkeyword'; + await docsifyInit(options); + await searchField.fill('currentkeyword'); + await expect(results).toHaveText('Current'); + await searchField.fill('retiredkeyword'); + await expect(page.getByText('No Results!', { exact: true })).toBeVisible(); + + await docsifyInit(options); + await searchField.fill('currentkeyword'); + await expect(results).toHaveText('Current'); + await searchField.fill('retiredkeyword'); + await expect(page.getByText('No Results!', { exact: true })).toBeVisible(); + }); + + test('preserves another namespace when saving a search index', async ({ + page, + }) => { + const otherSite = { + config: { search: { paths: ['/'], namespace: 'other-site' } }, + markdown: { homepage: '# Other site\n\nprotectedkeyword' }, + scriptURLs: ['/dist/plugins/search.js'], + }; + const searchField = page.locator('input[type=search]'); + const results = page.locator('.results-panel .title'); + + await docsifyInit(otherSite); + await searchField.fill('protectedkeyword'); + await expect(results).toHaveText('Other site'); + + await docsifyInit({ + config: { search: { paths: ['/'], namespace: 'current-site' } }, + markdown: { homepage: '# Current site\n\ncurrentkeyword' }, + scriptURLs: ['/dist/plugins/search.js'], + }); + await searchField.fill('currentkeyword'); + await expect(results).toHaveText('Current site'); + + otherSite.markdown.homepage = '# Changed\n\nchangedkeyword'; + await docsifyInit(otherSite); + await searchField.fill('protectedkeyword'); + await expect(results).toHaveText('Other site'); + }); + test('keeps saving index when one auto path request fails with cached records', async ({ page, }) => {