* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
@@ -1,344 +1,344 @@
|
||||
import SearchBar from "@/components/Menubar/EventSearchBar";
|
||||
import * as searchThunk from "@/features/Search/SearchSlice";
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import SearchBar from '@/components/Menubar/EventSearchBar'
|
||||
import * as searchThunk from '@/features/Search/SearchSlice'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
describe("EventSearchBar", () => {
|
||||
const today = new Date();
|
||||
const start = new Date(today);
|
||||
start.setHours(10, 0, 0, 0);
|
||||
const end = new Date(today);
|
||||
end.setHours(11, 0, 0, 0);
|
||||
describe('EventSearchBar', () => {
|
||||
const today = new Date()
|
||||
const start = new Date(today)
|
||||
start.setHours(10, 0, 0, 0)
|
||||
const end = new Date(today)
|
||||
end.setHours(11, 0, 0, 0)
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "mockSid",
|
||||
openpaasId: "user1",
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'mockSid',
|
||||
openpaasId: 'user1'
|
||||
},
|
||||
tokens: { accessToken: "token" },
|
||||
tokens: { accessToken: 'token' }
|
||||
},
|
||||
calendars: {
|
||||
list: {
|
||||
"user1/cal1": {
|
||||
name: "Calendar personal",
|
||||
id: "user1/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
'user1/cal1': {
|
||||
name: 'Calendar personal',
|
||||
id: 'user1/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user1/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user1/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"user2/cal1": {
|
||||
name: "Calendar delegated",
|
||||
'user2/cal1': {
|
||||
name: 'Calendar delegated',
|
||||
delegated: true,
|
||||
id: "user2/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
id: 'user2/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user2/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user2/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"user3/cal1": {
|
||||
name: "Calendar shared",
|
||||
id: "user3/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
'user3/cal1': {
|
||||
name: 'Calendar shared',
|
||||
id: 'user3/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user3/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user3/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
pending: false
|
||||
}
|
||||
}
|
||||
|
||||
it("should render search icon button initially", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should render search icon button initially', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
expect(searchButton).toBeInTheDocument();
|
||||
});
|
||||
const searchButton = screen.getByRole('button')
|
||||
expect(searchButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should expand search bar when icon is clicked", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should expand search bar when icon is clicked', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
expect(searchInput).toBeInTheDocument();
|
||||
});
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
expect(searchInput).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should unexpand search bar when field is unfocused and empty", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should unexpand search bar when field is unfocused and empty', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
fireEvent.mouseDown(document.body);
|
||||
expect(searchInput).not.toBeInTheDocument();
|
||||
});
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
fireEvent.mouseDown(document.body)
|
||||
expect(searchInput).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should not unexpand search bar when field is unfocused but not empty", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should not unexpand search bar when field is unfocused but not empty', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
userEvent.type(searchInput, "test");
|
||||
fireEvent.blur(searchInput);
|
||||
expect(searchInput).toBeInTheDocument();
|
||||
expect(searchInput).toHaveValue("test");
|
||||
});
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
userEvent.type(searchInput, 'test')
|
||||
fireEvent.blur(searchInput)
|
||||
expect(searchInput).toBeInTheDocument()
|
||||
expect(searchInput).toHaveValue('test')
|
||||
})
|
||||
|
||||
it("should update search value on input", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should update search value on input', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
fireEvent.change(searchInput, { target: { value: "meeting" } });
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
fireEvent.change(searchInput, { target: { value: 'meeting' } })
|
||||
|
||||
expect(searchInput).toHaveValue("meeting");
|
||||
});
|
||||
expect(searchInput).toHaveValue('meeting')
|
||||
})
|
||||
|
||||
it("should open filter popover when tune icon is clicked", async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should open filter popover when tune icon is clicked', async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
// Expand search bar
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
// Click tune icon
|
||||
const tuneButton = screen.getByTestId("TuneIcon");
|
||||
if (tuneButton) fireEvent.click(tuneButton);
|
||||
const tuneButton = screen.getByTestId('TuneIcon')
|
||||
if (tuneButton) fireEvent.click(tuneButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("search.searchIn")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
expect(screen.getByText('search.searchIn')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it("should clear search value when clear button is clicked", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('should clear search value when clear button is clicked', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
fireEvent.change(searchInput, { target: { value: "meeting" } });
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
fireEvent.change(searchInput, { target: { value: 'meeting' } })
|
||||
|
||||
const clearButton = screen.getByTestId("HighlightOffIcon");
|
||||
fireEvent.click(clearButton);
|
||||
const clearButton = screen.getByTestId('HighlightOffIcon')
|
||||
fireEvent.click(clearButton)
|
||||
|
||||
expect(searchInput).toHaveValue("");
|
||||
});
|
||||
expect(searchInput).toHaveValue('')
|
||||
})
|
||||
|
||||
it("should trigger search on Enter key", async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync");
|
||||
it('should trigger search on Enter key', async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
||||
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
fireEvent.change(searchInput, { target: { value: "test" } });
|
||||
fireEvent.keyDown(searchInput, { key: "Enter" });
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
fireEvent.change(searchInput, { target: { value: 'test' } })
|
||||
fireEvent.keyDown(searchInput, { key: 'Enter' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchSpy).toHaveBeenCalledWith({
|
||||
filters: {
|
||||
keywords: "",
|
||||
keywords: '',
|
||||
organizers: [],
|
||||
attendees: [],
|
||||
searchIn: ["user1/cal1"],
|
||||
searchIn: ['user1/cal1']
|
||||
},
|
||||
search: "test",
|
||||
});
|
||||
});
|
||||
});
|
||||
it("should not trigger search on Enter key when search is empty", async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync");
|
||||
search: 'test'
|
||||
})
|
||||
})
|
||||
})
|
||||
it('should not trigger search on Enter key when search is empty', async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
||||
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
const searchInput = screen.getByPlaceholderText("common.search");
|
||||
fireEvent.keyDown(searchInput, { key: "Enter" });
|
||||
const searchInput = screen.getByPlaceholderText('common.search')
|
||||
fireEvent.keyDown(searchInput, { key: 'Enter' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
expect(searchSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it("should not trigger search when filter keywords is empty", async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, "searchEventsAsync");
|
||||
it('should not trigger search when filter keywords is empty', async () => {
|
||||
const searchSpy = jest.spyOn(searchThunk, 'searchEventsAsync')
|
||||
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
const searchButton = screen.getByRole("button");
|
||||
fireEvent.click(searchButton);
|
||||
const searchButton = screen.getByRole('button')
|
||||
fireEvent.click(searchButton)
|
||||
|
||||
// Click tune icon
|
||||
const tuneButton = screen.getByTestId("TuneIcon");
|
||||
if (tuneButton) fireEvent.click(tuneButton);
|
||||
const tuneButton = screen.getByTestId('TuneIcon')
|
||||
if (tuneButton) fireEvent.click(tuneButton)
|
||||
|
||||
fireEvent.click(screen.getByText("common.search"));
|
||||
fireEvent.click(screen.getByText('common.search'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
it("keeps search bar expanded when popover opens", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
expect(searchSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
it('keeps search bar expanded when popover opens', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
const tuneBtn = screen
|
||||
.getAllByRole("button")
|
||||
.find((b) => b.querySelector('[data-testid="TuneIcon"]'));
|
||||
.getAllByRole('button')
|
||||
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
||||
|
||||
fireEvent.click(tuneBtn!);
|
||||
fireEvent.click(tuneBtn!)
|
||||
|
||||
expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("does NOT collapse search when clicking inside popover", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('does NOT collapse search when clicking inside popover', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
const tuneBtn = screen
|
||||
.getAllByRole("button")
|
||||
.find((b) => b.querySelector('[data-testid="TuneIcon"]'));
|
||||
.getAllByRole('button')
|
||||
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
||||
|
||||
fireEvent.click(tuneBtn!);
|
||||
fireEvent.click(tuneBtn!)
|
||||
|
||||
fireEvent.mouseDown(screen.getByText("search.searchIn"));
|
||||
fireEvent.mouseDown(screen.getByText('search.searchIn'))
|
||||
|
||||
expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("clicking Cancel closes popover and search collapses if empty", async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('clicking Cancel closes popover and search collapses if empty', async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
const tuneBtn = screen
|
||||
.getAllByRole("button")
|
||||
.find((b) => b.querySelector('[data-testid="TuneIcon"]'));
|
||||
.getAllByRole('button')
|
||||
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
||||
|
||||
fireEvent.click(tuneBtn!);
|
||||
fireEvent.click(tuneBtn!)
|
||||
|
||||
fireEvent.click(screen.getByText("common.cancel"));
|
||||
fireEvent.click(screen.getByText('common.cancel'))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByText("search.searchIn")).not.toBeInTheDocument()
|
||||
);
|
||||
expect(screen.queryByPlaceholderText("common.search")).toBeNull();
|
||||
});
|
||||
expect(screen.queryByText('search.searchIn')).not.toBeInTheDocument()
|
||||
)
|
||||
expect(screen.queryByPlaceholderText('common.search')).toBeNull()
|
||||
})
|
||||
|
||||
it("does not collapse when selecting a filter value", async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('does not collapse when selecting a filter value', async () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
const tuneBtn = screen
|
||||
.getAllByRole("button")
|
||||
.find((b) => b.querySelector('[data-testid="TuneIcon"]'));
|
||||
.getAllByRole('button')
|
||||
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
||||
|
||||
fireEvent.click(tuneBtn!);
|
||||
fireEvent.click(tuneBtn!)
|
||||
|
||||
fireEvent.mouseDown(screen.getByText("search.searchIn")); // simulate interaction inside menu
|
||||
fireEvent.mouseDown(screen.getByText('search.searchIn')) // simulate interaction inside menu
|
||||
|
||||
expect(screen.getByPlaceholderText("common.search")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByPlaceholderText('common.search')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("keeps input value after popover closes if not empty", () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState);
|
||||
it('keeps input value after popover closes if not empty', () => {
|
||||
renderWithProviders(<SearchBar />, preloadedState)
|
||||
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
userEvent.type(screen.getByPlaceholderText("common.search"), "hello");
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
userEvent.type(screen.getByPlaceholderText('common.search'), 'hello')
|
||||
|
||||
const tuneBtn = screen
|
||||
.getAllByRole("button")
|
||||
.find((b) => b.querySelector('[data-testid="TuneIcon"]'));
|
||||
.getAllByRole('button')
|
||||
.find(b => b.querySelector('[data-testid="TuneIcon"]'))
|
||||
|
||||
fireEvent.click(tuneBtn!);
|
||||
fireEvent.click(document.body);
|
||||
fireEvent.click(tuneBtn!)
|
||||
fireEvent.click(document.body)
|
||||
|
||||
const inputAfter = screen.getByPlaceholderText("common.search");
|
||||
expect(inputAfter).toHaveValue("hello");
|
||||
});
|
||||
});
|
||||
const inputAfter = screen.getByPlaceholderText('common.search')
|
||||
expect(inputAfter).toHaveValue('hello')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,224 +1,224 @@
|
||||
import SearchResultsPage from "@/features/Search/SearchResultsPage";
|
||||
import { screen } from "@testing-library/react";
|
||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||
import SearchResultsPage from '@/features/Search/SearchResultsPage'
|
||||
import { screen } from '@testing-library/react'
|
||||
import { renderWithProviders } from '../../utils/Renderwithproviders'
|
||||
|
||||
describe("SearchResultsPage", () => {
|
||||
const today = new Date();
|
||||
const start = new Date(today);
|
||||
start.setHours(10, 0, 0, 0);
|
||||
const end = new Date(today);
|
||||
end.setHours(11, 0, 0, 0);
|
||||
describe('SearchResultsPage', () => {
|
||||
const today = new Date()
|
||||
const start = new Date(today)
|
||||
start.setHours(10, 0, 0, 0)
|
||||
const end = new Date(today)
|
||||
end.setHours(11, 0, 0, 0)
|
||||
const preloadedState = {
|
||||
user: {
|
||||
userData: {
|
||||
sub: "test",
|
||||
email: "test@test.com",
|
||||
sid: "mockSid",
|
||||
openpaasId: "user1",
|
||||
sub: 'test',
|
||||
email: 'test@test.com',
|
||||
sid: 'mockSid',
|
||||
openpaasId: 'user1'
|
||||
},
|
||||
tokens: { accessToken: "token" },
|
||||
tokens: { accessToken: 'token' }
|
||||
},
|
||||
calendars: {
|
||||
list: {
|
||||
"user1/cal1": {
|
||||
name: "Calendar personal",
|
||||
id: "user1/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
'user1/cal1': {
|
||||
name: 'Calendar personal',
|
||||
id: 'user1/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user1/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user1/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"user2/cal1": {
|
||||
name: "Calendar delegated",
|
||||
'user2/cal1': {
|
||||
name: 'Calendar delegated',
|
||||
delegated: true,
|
||||
id: "user2/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
id: 'user2/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user2/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user2/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"user3/cal1": {
|
||||
name: "Calendar shared",
|
||||
id: "user3/cal1",
|
||||
color: { light: "#FF0000", dark: "#000" },
|
||||
owner: { emails: ["alice@example.com"] },
|
||||
'user3/cal1': {
|
||||
name: 'Calendar shared',
|
||||
id: 'user3/cal1',
|
||||
color: { light: '#FF0000', dark: '#000' },
|
||||
owner: { emails: ['alice@example.com'] },
|
||||
events: {
|
||||
event1: {
|
||||
id: "event1",
|
||||
calId: "user3/cal1",
|
||||
uid: "event1",
|
||||
title: "Test Event",
|
||||
id: 'event1',
|
||||
calId: 'user3/cal1',
|
||||
uid: 'event1',
|
||||
title: 'Test Event',
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
partstat: "ACCEPTED",
|
||||
partstat: 'ACCEPTED',
|
||||
organizer: {
|
||||
cn: "Alice",
|
||||
cal_address: "alice@example.com",
|
||||
cn: 'Alice',
|
||||
cal_address: 'alice@example.com'
|
||||
},
|
||||
attendee: [
|
||||
{
|
||||
cn: "Alice",
|
||||
partstat: "ACCEPTED",
|
||||
rsvp: "TRUE",
|
||||
role: "REQ-PARTICIPANT",
|
||||
cutype: "INDIVIDUAL",
|
||||
cal_address: "alice@example.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
cn: 'Alice',
|
||||
partstat: 'ACCEPTED',
|
||||
rsvp: 'TRUE',
|
||||
role: 'REQ-PARTICIPANT',
|
||||
cutype: 'INDIVIDUAL',
|
||||
cal_address: 'alice@example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pending: false,
|
||||
},
|
||||
};
|
||||
pending: false
|
||||
}
|
||||
}
|
||||
|
||||
it("should show loading spinner when loading", () => {
|
||||
it('should show loading spinner when loading', () => {
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: { loading: true, error: null, hits: 0, results: [] },
|
||||
});
|
||||
expect(screen.getByRole("progressbar")).toBeInTheDocument();
|
||||
});
|
||||
searchResult: { loading: true, error: null, hits: 0, results: [] }
|
||||
})
|
||||
expect(screen.getByRole('progressbar')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should show error message when error occurs", () => {
|
||||
it('should show error message when error occurs', () => {
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: {
|
||||
loading: false,
|
||||
error: "Network error",
|
||||
error: 'Network error',
|
||||
hits: 0,
|
||||
results: [],
|
||||
},
|
||||
});
|
||||
expect(screen.getByText("Network error")).toBeInTheDocument();
|
||||
});
|
||||
results: []
|
||||
}
|
||||
})
|
||||
expect(screen.getByText('Network error')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should show no results message when no hits", () => {
|
||||
it('should show no results message when no hits', () => {
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: { loading: false, error: null, hits: null, results: [] },
|
||||
});
|
||||
expect(screen.getByText("search.noResults")).toBeInTheDocument();
|
||||
});
|
||||
searchResult: { loading: false, error: null, hits: null, results: [] }
|
||||
})
|
||||
expect(screen.getByText('search.noResults')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should render search results", () => {
|
||||
it('should render search results', () => {
|
||||
const mockResults = [
|
||||
{
|
||||
data: {
|
||||
uid: "1",
|
||||
summary: "Team Meeting",
|
||||
start: "2025-06-26T15:00:00Z",
|
||||
organizer: { cn: "John Doe", email: "john@example.com" },
|
||||
allDay: false,
|
||||
},
|
||||
uid: '1',
|
||||
summary: 'Team Meeting',
|
||||
start: '2025-06-26T15:00:00Z',
|
||||
organizer: { cn: 'John Doe', email: 'john@example.com' },
|
||||
allDay: false
|
||||
}
|
||||
},
|
||||
{
|
||||
data: {
|
||||
uid: "2",
|
||||
summary: "Project Review",
|
||||
start: "2025-06-27T10:00:00Z",
|
||||
organizer: { cn: "Jane Smith", email: "jane@example.com" },
|
||||
allDay: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
uid: '2',
|
||||
summary: 'Project Review',
|
||||
start: '2025-06-27T10:00:00Z',
|
||||
organizer: { cn: 'Jane Smith', email: 'jane@example.com' },
|
||||
allDay: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: { results: mockResults, hits: 2 },
|
||||
});
|
||||
expect(screen.getByText("search.resultsTitle")).toBeInTheDocument();
|
||||
expect(screen.getByText("Team Meeting")).toBeInTheDocument();
|
||||
expect(screen.getByText("Project Review")).toBeInTheDocument();
|
||||
expect(screen.getByText("John Doe")).toBeInTheDocument();
|
||||
expect(screen.getByText("Jane Smith")).toBeInTheDocument();
|
||||
});
|
||||
searchResult: { results: mockResults, hits: 2 }
|
||||
})
|
||||
expect(screen.getByText('search.resultsTitle')).toBeInTheDocument()
|
||||
expect(screen.getByText('Team Meeting')).toBeInTheDocument()
|
||||
expect(screen.getByText('Project Review')).toBeInTheDocument()
|
||||
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
||||
expect(screen.getByText('Jane Smith')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should handle events without organizer", () => {
|
||||
it('should handle events without organizer', () => {
|
||||
const mockResults = [
|
||||
{
|
||||
data: {
|
||||
uid: "1",
|
||||
summary: "Untitled Event",
|
||||
start: "2025-06-26T15:00:00Z",
|
||||
allDay: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
uid: '1',
|
||||
summary: 'Untitled Event',
|
||||
start: '2025-06-26T15:00:00Z',
|
||||
allDay: false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: { results: mockResults, hits: 1 },
|
||||
});
|
||||
searchResult: { results: mockResults, hits: 1 }
|
||||
})
|
||||
|
||||
expect(screen.getByText("Untitled Event")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('Untitled Event')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should format all-day events correctly", () => {
|
||||
it('should format all-day events correctly', () => {
|
||||
const mockResults = [
|
||||
{
|
||||
data: {
|
||||
uid: "1",
|
||||
summary: "All Day Event",
|
||||
start: "2025-06-26T00:00:00Z",
|
||||
organizer: { cn: "Organizer" },
|
||||
allDay: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
uid: '1',
|
||||
summary: 'All Day Event',
|
||||
start: '2025-06-26T00:00:00Z',
|
||||
organizer: { cn: 'Organizer' },
|
||||
allDay: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
renderWithProviders(<SearchResultsPage />, {
|
||||
...preloadedState,
|
||||
searchResult: { results: mockResults, hits: 1 },
|
||||
});
|
||||
searchResult: { results: mockResults, hits: 1 }
|
||||
})
|
||||
|
||||
expect(screen.getByText("All Day Event")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
expect(screen.getByText('All Day Event')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,126 +1,126 @@
|
||||
import * as EventApi from "@/features/Events/EventApi";
|
||||
import * as EventApi from '@/features/Events/EventApi'
|
||||
import searchResultReducer, {
|
||||
searchEventsAsync,
|
||||
setHits,
|
||||
setResults,
|
||||
} from "@/features/Search/SearchSlice";
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
setResults
|
||||
} from '@/features/Search/SearchSlice'
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
|
||||
jest.mock("@/features/Events/EventApi");
|
||||
jest.mock('@/features/Events/EventApi')
|
||||
|
||||
describe("SearchSlice", () => {
|
||||
let store: any;
|
||||
describe('SearchSlice', () => {
|
||||
let store: any
|
||||
|
||||
beforeEach(() => {
|
||||
store = configureStore({
|
||||
reducer: {
|
||||
searchResult: searchResultReducer,
|
||||
},
|
||||
});
|
||||
});
|
||||
searchResult: searchResultReducer
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("initial state", () => {
|
||||
it("should have correct initial state", () => {
|
||||
const state = store.getState().searchResult;
|
||||
describe('initial state', () => {
|
||||
it('should have correct initial state', () => {
|
||||
const state = store.getState().searchResult
|
||||
expect(state).toEqual({
|
||||
results: [],
|
||||
hits: 0,
|
||||
error: null,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
loading: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("reducers", () => {
|
||||
it("should handle setResults", () => {
|
||||
describe('reducers', () => {
|
||||
it('should handle setResults', () => {
|
||||
const mockResults = [
|
||||
{ uid: "1", summary: "Event 1" },
|
||||
{ uid: "2", summary: "Event 2" },
|
||||
];
|
||||
store.dispatch(setResults(mockResults as any));
|
||||
expect(store.getState().searchResult.results).toEqual(mockResults);
|
||||
});
|
||||
{ uid: '1', summary: 'Event 1' },
|
||||
{ uid: '2', summary: 'Event 2' }
|
||||
]
|
||||
store.dispatch(setResults(mockResults as any))
|
||||
expect(store.getState().searchResult.results).toEqual(mockResults)
|
||||
})
|
||||
|
||||
it("should handle setHits", () => {
|
||||
store.dispatch(setHits(42));
|
||||
expect(store.getState().searchResult.hits).toBe(42);
|
||||
});
|
||||
});
|
||||
it('should handle setHits', () => {
|
||||
store.dispatch(setHits(42))
|
||||
expect(store.getState().searchResult.hits).toBe(42)
|
||||
})
|
||||
})
|
||||
|
||||
describe("searchEventsAsync", () => {
|
||||
describe('searchEventsAsync', () => {
|
||||
const mockFilters = {
|
||||
searchIn: ["user1/calendar1"],
|
||||
keywords: "meeting",
|
||||
organizers: ["user@example.com"],
|
||||
attendees: ["attendee@example.com"],
|
||||
};
|
||||
searchIn: ['user1/calendar1'],
|
||||
keywords: 'meeting',
|
||||
organizers: ['user@example.com'],
|
||||
attendees: ['attendee@example.com']
|
||||
}
|
||||
|
||||
it("should handle successful search", async () => {
|
||||
it('should handle successful search', async () => {
|
||||
const mockResponse = {
|
||||
_total_hits: 5,
|
||||
_embedded: {
|
||||
events: [
|
||||
{ data: { uid: "1", summary: "Test Event" } },
|
||||
{ data: { uid: "2", summary: "Another Event" } },
|
||||
],
|
||||
},
|
||||
};
|
||||
{ data: { uid: '1', summary: 'Test Event' } },
|
||||
{ data: { uid: '2', summary: 'Another Event' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
(EventApi.searchEvent as jest.Mock).mockResolvedValue(mockResponse);
|
||||
;(EventApi.searchEvent as jest.Mock).mockResolvedValue(mockResponse)
|
||||
|
||||
await store.dispatch(
|
||||
searchEventsAsync({ search: "test", filters: mockFilters })
|
||||
);
|
||||
searchEventsAsync({ search: 'test', filters: mockFilters })
|
||||
)
|
||||
|
||||
const state = store.getState().searchResult;
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.hits).toBe(5);
|
||||
expect(state.results).toEqual(mockResponse._embedded.events);
|
||||
expect(state.error).toBeNull();
|
||||
});
|
||||
const state = store.getState().searchResult
|
||||
expect(state.loading).toBe(false)
|
||||
expect(state.hits).toBe(5)
|
||||
expect(state.results).toEqual(mockResponse._embedded.events)
|
||||
expect(state.error).toBeNull()
|
||||
})
|
||||
|
||||
it("should handle search with no results", async () => {
|
||||
it('should handle search with no results', async () => {
|
||||
const mockResponse = {
|
||||
_total_hits: 0,
|
||||
_embedded: { events: [] },
|
||||
};
|
||||
_embedded: { events: [] }
|
||||
}
|
||||
|
||||
(EventApi.searchEvent as jest.Mock).mockResolvedValue(mockResponse);
|
||||
;(EventApi.searchEvent as jest.Mock).mockResolvedValue(mockResponse)
|
||||
|
||||
await store.dispatch(
|
||||
searchEventsAsync({ search: "nonexistent", filters: mockFilters })
|
||||
);
|
||||
searchEventsAsync({ search: 'nonexistent', filters: mockFilters })
|
||||
)
|
||||
|
||||
const state = store.getState().searchResult;
|
||||
expect(state.hits).toBe(0);
|
||||
expect(state.results).toEqual([]);
|
||||
});
|
||||
const state = store.getState().searchResult
|
||||
expect(state.hits).toBe(0)
|
||||
expect(state.results).toEqual([])
|
||||
})
|
||||
|
||||
it("should handle search error", async () => {
|
||||
const mockError = new Error("Network error");
|
||||
(EventApi.searchEvent as jest.Mock).mockRejectedValue(mockError);
|
||||
it('should handle search error', async () => {
|
||||
const mockError = new Error('Network error')
|
||||
;(EventApi.searchEvent as jest.Mock).mockRejectedValue(mockError)
|
||||
|
||||
await store.dispatch(
|
||||
searchEventsAsync({ search: "test", filters: mockFilters })
|
||||
);
|
||||
searchEventsAsync({ search: 'test', filters: mockFilters })
|
||||
)
|
||||
|
||||
const state = store.getState().searchResult;
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBeTruthy();
|
||||
expect(state.results).toEqual([]);
|
||||
});
|
||||
const state = store.getState().searchResult
|
||||
expect(state.loading).toBe(false)
|
||||
expect(state.error).toBeTruthy()
|
||||
expect(state.results).toEqual([])
|
||||
})
|
||||
|
||||
it("should set loading state during search", async () => {
|
||||
(EventApi.searchEvent as jest.Mock).mockImplementation(
|
||||
() => new Promise((resolve) => setTimeout(resolve, 100))
|
||||
);
|
||||
it('should set loading state during search', async () => {
|
||||
;(EventApi.searchEvent as jest.Mock).mockImplementation(
|
||||
() => new Promise(resolve => setTimeout(resolve, 100))
|
||||
)
|
||||
|
||||
const promise = store.dispatch(
|
||||
searchEventsAsync({ search: "test", filters: mockFilters })
|
||||
);
|
||||
searchEventsAsync({ search: 'test', filters: mockFilters })
|
||||
)
|
||||
|
||||
expect(store.getState().searchResult.loading).toBe(true);
|
||||
await promise;
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(store.getState().searchResult.loading).toBe(true)
|
||||
await promise
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user