Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recommendation Back/Forward Cache #1208

Merged
merged 3 commits into from
Dec 4, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { EventManager } from '@searchspring/snap-event-manager';
import { Profiler } from '@searchspring/snap-profiler';
import { Logger } from '@searchspring/snap-logger';
import { MockClient } from '@searchspring/snap-shared';

import { waitFor } from '@testing-library/preact';
import { RecommendationController } from './RecommendationController';

const globals = { siteId: '8uyt2m' };
Expand Down Expand Up @@ -118,6 +118,135 @@ describe('Recommendation Controller', () => {
});
});

it(`tests searchOnPageShow triggers search on persisted pageshow event `, async function () {
const controller = new RecommendationController(recommendConfig, {
client: new MockClient(globals, {}),
store: new RecommendationStore(recommendConfig, services),
urlManager,
eventManager: new EventManager(),
profiler: new Profiler(),
logger: new Logger(),
tracker: new Tracker(globals),
});

await controller.search();

const searchSpy = jest.spyOn(controller, 'search');

expect(searchSpy).not.toHaveBeenCalled();

// Mock PageTransitionEvent
class MockPageTransitionEvent extends Event {
public persisted: boolean;

constructor(type: string, eventInitDict?: EventInit & { persisted?: boolean }) {
super(type, eventInitDict);
this.persisted = eventInitDict?.persisted ?? false;
}
}

const event = new MockPageTransitionEvent('pageshow', {
bubbles: true,
persisted: true,
});

window.dispatchEvent(event);

await waitFor(() => {
expect(searchSpy).toHaveBeenCalled();
});
});

it(`can turn off searchOnPageShow`, async function () {
const customConfig = {
...recommendConfig,
settings: {
searchOnPageShow: false,
},
};
const controller = new RecommendationController(customConfig, {
client: new MockClient(globals, {}),
store: new RecommendationStore(recommendConfig, services),
urlManager,
eventManager: new EventManager(),
profiler: new Profiler(),
logger: new Logger(),
tracker: new Tracker(globals),
});

await controller.search();

const searchSpy = jest.spyOn(controller, 'search');

expect(searchSpy).not.toHaveBeenCalled();

// Mock PageTransitionEvent
class MockPageTransitionEvent extends Event {
public persisted: boolean;

constructor(type: string, eventInitDict?: EventInit & { persisted?: boolean }) {
super(type, eventInitDict);
this.persisted = eventInitDict?.persisted ?? false;
}
}

const event = new MockPageTransitionEvent('pageshow', {
bubbles: true,
persisted: true,
});

window.dispatchEvent(event);

await waitFor(() => {
expect(searchSpy).not.toHaveBeenCalled();
});
});

it(`tests searchOnPageShow doesnt trigger search if persisted is false or undefined on the pageshow event`, async function () {
const controller = new RecommendationController(recommendConfig, {
client: new MockClient(globals, {}),
store: new RecommendationStore(recommendConfig, services),
urlManager,
eventManager: new EventManager(),
profiler: new Profiler(),
logger: new Logger(),
tracker: new Tracker(globals),
});

await controller.search();

const searchSpy = jest.spyOn(controller, 'search');

expect(searchSpy).not.toHaveBeenCalled();

// Mock PageTransitionEvent
class MockPageTransitionEvent extends Event {
public persisted: boolean;

constructor(type: string, eventInitDict?: EventInit & { persisted?: boolean }) {
super(type, eventInitDict);
this.persisted = eventInitDict?.persisted ?? false;
}
}

const event = new MockPageTransitionEvent('pageshow', {
bubbles: true,
persisted: false,
});

window.dispatchEvent(event);

const event2 = new MockPageTransitionEvent('pageshow', {
bubbles: true,
});

window.dispatchEvent(event2);

await waitFor(() => {
expect(searchSpy).not.toHaveBeenCalled();
});
});

it('can invoke controller track.click and track.product.click', async () => {
const controller = new RecommendationController(recommendConfig, {
client: new MockClient(globals, {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ export class RecommendationController extends AbstractController {
throw new Error(`Invalid config passed to RecommendationController. The "tag" attribute is required.`);
}

// attach to bfCache restore event and re-run search on the controller
// enabled by default
if (config.settings?.searchOnPageShow !== false) {
window.addEventListener('pageshow', (e) => {
if (e.persisted && !this.store.error && this.store.loaded && !this.store.loading) {
this.search();
}
});
}

// deep merge config with defaults
this.config = deepmerge(defaultConfig, this.config);
this.store.setConfig(this.config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cookies } from '@searchspring/snap-toolbox';
import { Logger } from '@searchspring/snap-logger';
import { MockClient } from '@searchspring/snap-shared';
import { Next } from '@searchspring/snap-event-manager';
import { waitFor } from '@testing-library/preact';

const DEFAULT_PROFILE = 'trending';
const CART_COOKIE = 'ssCartProducts';
Expand Down Expand Up @@ -936,4 +937,45 @@ describe('RecommendationInstantiator', () => {
expect(plugin2).toHaveBeenCalledWith(controller);
});
});

it(`searchOnPageShow triggers search on persisted pageshow event `, async function () {
document.body.innerHTML = `<script type="searchspring/recommend" profile="${DEFAULT_PROFILE}"></script>`;

const attachmentConfig = {
...baseConfig,
config: {
branch: baseConfig.config.branch,
},
};

const client = new MockClient(baseConfig.client!.globals, {});
const recommendationInstantiator = new RecommendationInstantiator(attachmentConfig as RecommendationInstantiatorConfig, { client });
await wait();

Object.keys(recommendationInstantiator.controller).forEach(async (controllerId) => {
const controller = recommendationInstantiator.controller[controllerId];
const searchSpy = jest.spyOn(controller, 'search');
expect(searchSpy).not.toHaveBeenCalled();

// Mock PageTransitionEvent
class MockPageTransitionEvent extends Event {
public persisted: boolean;

constructor(type: string, eventInitDict?: EventInit & { persisted?: boolean }) {
super(type, eventInitDict);
this.persisted = eventInitDict?.persisted ?? false;
}
}
const event = new MockPageTransitionEvent('pageshow', {
bubbles: true,
persisted: true,
});

window.dispatchEvent(event);

await waitFor(() => {
expect(searchSpy).toHaveBeenCalled();
});
});
});
});
1 change: 1 addition & 0 deletions packages/snap-store-mobx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export type RecommendationStoreConfig = StoreConfig & {
batchId?: number;
settings?: {
variants?: VariantConfig;
searchOnPageShow: boolean;
};
};

Expand Down
Loading