Skip to content

Commit 7d9d4b2

Browse files
DanielRyanSmithDanielRyanSmith
and
DanielRyanSmith
authored
Move saved-search-editor up to parent component (#1487)
* Move saved search editor to overview-content * remove unused properties * lint * update to use trigger open using events * only overview typeahead has 'inputfield' ID * define CustomEvent interface for opening editor * pass input ID into typeahead --------- Co-authored-by: DanielRyanSmith <[email protected]>
1 parent e0b71b3 commit 7d9d4b2

10 files changed

+411
-243
lines changed

frontend/src/static/js/components/test/webstatus-overview-content.test.ts

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,119 @@
1616

1717
import {WebstatusOverviewContent} from '../webstatus-overview-content.js';
1818
import '../webstatus-overview-content.js';
19-
import {expect} from '@open-wc/testing';
19+
import {elementUpdated, expect, fixture, html} from '@open-wc/testing';
20+
21+
import {APIClient} from '../../api/client.js';
22+
23+
import {stub} from 'sinon'; // Make sure you have sinon installed
24+
import {savedSearchHelpers} from '../../contexts/app-bookmark-info-context.js';
25+
import sinon from 'sinon';
26+
import {User} from 'firebase/auth';
27+
import {WebstatusSavedSearchEditor} from '../webstatus-saved-search-editor.js';
28+
import {
29+
BookmarkOwnerRole,
30+
BookmarkStatusActive,
31+
UserSavedSearch,
32+
} from '../../utils/constants.js';
2033

2134
describe('webstatus-overview-content', () => {
35+
let element: WebstatusOverviewContent;
36+
let apiClientMock: sinon.SinonStubbedInstance<APIClient>;
37+
let userMock: User;
38+
let editor: WebstatusSavedSearchEditor;
39+
let editorIsOpenStub: sinon.SinonStub;
40+
let editorOpenSpy: sinon.SinonSpy;
41+
let getEditSavedSearchStub: sinon.SinonStub;
42+
let updatePageUrlStub: sinon.SinonStub;
43+
44+
const mockSavedSearchOwner: UserSavedSearch = {
45+
id: 'owner123',
46+
name: 'My Search',
47+
query: 'feature:css',
48+
description: 'A search I own',
49+
created_at: new Date().toISOString(),
50+
updated_at: new Date().toISOString(),
51+
permissions: {role: BookmarkOwnerRole},
52+
bookmark_status: {status: BookmarkStatusActive}, // Owners always have it bookmarked implicitly
53+
};
54+
55+
const mockLocation = {search: '?q=feature:css'};
56+
57+
beforeEach(async () => {
58+
apiClientMock = sinon.createStubInstance(APIClient);
59+
userMock = {
60+
getIdToken: sinon.stub().resolves('mock-token'),
61+
} as unknown as User;
62+
63+
element = await fixture<WebstatusOverviewContent>(html`
64+
<webstatus-overview-content
65+
.apiClient=${apiClientMock}
66+
.user=${userMock}
67+
.location=${mockLocation}
68+
>
69+
</webstatus-overview-content>
70+
`);
71+
72+
element._getOrigin = () => 'http://localhost:8080';
73+
74+
getEditSavedSearchStub = sinon
75+
.stub(element, '_getEditSavedSearch')
76+
.returns(false);
77+
updatePageUrlStub = sinon.stub(element, '_updatePageUrl');
78+
// Get the mocked editor instance after the element is rendered
79+
editor = element.shadowRoot!.querySelector<WebstatusSavedSearchEditor>(
80+
'webstatus-saved-search-editor',
81+
)!;
82+
editorOpenSpy = sinon.spy(editor, 'open');
83+
editorIsOpenStub = sinon.stub(editor, 'isOpen');
84+
});
85+
86+
afterEach(() => {
87+
sinon.restore();
88+
});
89+
90+
it('should correctly update activeQuery based on getCurrentQuery return value', async () => {
91+
const apiClient = new APIClient('');
92+
const location = {search: ''};
93+
94+
const getCurrentQueryStub = stub(savedSearchHelpers, 'getCurrentQuery');
95+
96+
// Test case 1: Empty query
97+
getCurrentQueryStub.returns('');
98+
let component = await fixture<WebstatusOverviewContent>(
99+
html`<webstatus-overview-content
100+
.location=${location}
101+
.apiClient=${apiClient}
102+
></webstatus-overview-content>`,
103+
);
104+
await elementUpdated(component);
105+
expect(component.activeQuery).to.eq('');
106+
107+
// Test case 2: A specific query
108+
getCurrentQueryStub.returns('my-test-query');
109+
component = await fixture<WebstatusOverviewContent>(
110+
html`<webstatus-overview-content
111+
.location=${location}
112+
.apiClient=${apiClient}
113+
></webstatus-overview-content>`,
114+
);
115+
await elementUpdated(component);
116+
expect(component.activeQuery).to.eq('my-test-query');
117+
118+
// Test case 3: Another query
119+
getCurrentQueryStub.returns('another-test-query');
120+
component = await fixture<WebstatusOverviewContent>(
121+
html`<webstatus-overview-content
122+
.location=${location}
123+
.apiClient=${apiClient}
124+
></webstatus-overview-content>`,
125+
);
126+
await elementUpdated(component);
127+
expect(component.activeQuery).to.eq('another-test-query');
128+
129+
getCurrentQueryStub.restore();
130+
});
131+
22132
describe('RenderBookmarkUI', () => {
23133
let container: HTMLElement;
24134
afterEach(() => {
@@ -104,4 +214,92 @@ describe('webstatus-overview-content', () => {
104214
expect(description).to.not.exist;
105215
});
106216
});
217+
218+
describe('updated lifecycle hook', () => {
219+
it('opens edit dialog and updates URL if edit_saved_search param is present', async () => {
220+
element.location = {search: 'test'};
221+
element.appBookmarkInfo = {
222+
globalSavedSearches: [
223+
{
224+
name: 'Test Bookmark 1',
225+
query: 'test_query_1',
226+
description: 'test description1',
227+
},
228+
{
229+
name: 'Test Bookmark 2',
230+
query: 'test_query_2',
231+
description: 'test description2',
232+
},
233+
],
234+
currentGlobalSavedSearch: {
235+
name: 'Test Bookmark 1',
236+
query: 'test_query_1',
237+
description: 'test description1',
238+
},
239+
};
240+
element.savedSearch = {...mockSavedSearchOwner};
241+
getEditSavedSearchStub.returns(true); // Simulate finding the param
242+
editorIsOpenStub.returns(false); // Simulate editor not already open
243+
244+
// Trigger the updated lifecycle hook manually for testing
245+
element.requestUpdate();
246+
await element.updateComplete;
247+
248+
// It should call openSavedSearchDialog, which calls editor.open
249+
expect(editorOpenSpy).to.have.been.calledOnceWith(
250+
'edit',
251+
element.savedSearch,
252+
element.savedSearch.query,
253+
);
254+
// It should remove the URL parameter
255+
expect(updatePageUrlStub).to.have.been.calledOnceWith(
256+
'',
257+
element.location,
258+
{edit_saved_search: undefined},
259+
);
260+
});
261+
262+
it('does not open edit dialog if editor is already open', async () => {
263+
element.savedSearch = {...mockSavedSearchOwner};
264+
getEditSavedSearchStub.returns(true);
265+
editorIsOpenStub.returns(true);
266+
267+
element.requestUpdate();
268+
await element.updateComplete;
269+
270+
expect(editorOpenSpy).to.not.have.been.called;
271+
// Should not update URL if dialog wasn't opened by this hook
272+
expect(updatePageUrlStub).to.not.have.been.called;
273+
expect(getEditSavedSearchStub).to.have.been.called;
274+
});
275+
276+
it('does not open edit dialog if edit_saved_search param is not present', async () => {
277+
element.savedSearch = {...mockSavedSearchOwner};
278+
getEditSavedSearchStub.returns(false); // Param not present
279+
280+
editorIsOpenStub.returns(false);
281+
282+
element.requestUpdate();
283+
await element.updateComplete;
284+
285+
expect(editorIsOpenStub).to.not.have.been.called;
286+
expect(updatePageUrlStub).to.not.have.been.called;
287+
});
288+
289+
it('does not open edit dialog if savedSearch is not available', async () => {
290+
element.savedSearch = undefined; // No saved search loaded yet
291+
getEditSavedSearchStub.returns(true);
292+
editorIsOpenStub.returns(false);
293+
294+
element.requestUpdate();
295+
await element.updateComplete;
296+
297+
expect(editorOpenSpy).to.not.have.been.called;
298+
// updatePageUrl might still be called depending on exact logic,
299+
// but the primary action (opening dialog) shouldn't happen.
300+
// Let's assert it's not called for clarity, though the original code
301+
// might call it regardless. The important part is the dialog doesn't open.
302+
expect(updatePageUrlStub).to.not.have.been.called;
303+
});
304+
});
107305
});

frontend/src/static/js/components/test/webstatus-overview-filters.test.ts

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,51 +30,6 @@ import {Toast} from '../../utils/toast.js';
3030
import {WebstatusOverviewFilters} from '../webstatus-overview-filters.js';
3131
import {APIClient} from '../../api/client.js';
3232

33-
import {stub} from 'sinon'; // Make sure you have sinon installed
34-
import {savedSearchHelpers} from '../../contexts/app-bookmark-info-context.js';
35-
36-
it('should correctly update _activeQuery based on getCurrentQuery return value', async () => {
37-
const apiClient = new APIClient('');
38-
const location = {search: ''};
39-
40-
const getCurrentQueryStub = stub(savedSearchHelpers, 'getCurrentQuery');
41-
42-
// Test case 1: Empty query
43-
getCurrentQueryStub.returns('');
44-
let filterComponent = await fixture<WebstatusOverviewFilters>(
45-
html`<webstatus-overview-filters
46-
.location=${location}
47-
.apiClient=${apiClient}
48-
></webstatus-overview-filters>`,
49-
);
50-
await elementUpdated(filterComponent);
51-
expect(filterComponent._activeQuery).to.eq('');
52-
53-
// Test case 2: A specific query
54-
getCurrentQueryStub.returns('my-test-query');
55-
filterComponent = await fixture<WebstatusOverviewFilters>(
56-
html`<webstatus-overview-filters
57-
.location=${location}
58-
.apiClient=${apiClient}
59-
></webstatus-overview-filters>`,
60-
);
61-
await elementUpdated(filterComponent);
62-
expect(filterComponent._activeQuery).to.eq('my-test-query');
63-
64-
// Test case 3: Another query
65-
getCurrentQueryStub.returns('another-test-query');
66-
filterComponent = await fixture<WebstatusOverviewFilters>(
67-
html`<webstatus-overview-filters
68-
.location=${location}
69-
.apiClient=${apiClient}
70-
></webstatus-overview-filters>`,
71-
);
72-
await elementUpdated(filterComponent);
73-
expect(filterComponent._activeQuery).to.eq('another-test-query');
74-
75-
getCurrentQueryStub.restore();
76-
});
77-
7833
describe('downloadCSV', () => {
7934
it('should display an error toast when the CSVUtils.downloadCSV function throws an error', async () => {
8035
const apiClient = new APIClient(''); // TODO Can probably stub allFeaturesFetecher instead.

0 commit comments

Comments
 (0)