feat: the library's albums go on the wanted list as held, once; after that the list is the truth (M502 #5567)
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
A one-time job in the wanted worker lists every album in the library as a held want, one per release group, with a record that it happened (acquisition_settings.library_listed_at). Albums whose tags name only a release are named on MusicBrainz first, 120 a pass; what nothing names is counted and shown on the Held tab. Re-acquisition now wants back only albums on the list. Taking a held album off the list leaves its files, and it is never fetched back; albums put in the library folder by hand afterwards are not added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,9 @@
|
||||
// One tab of the Wanted view (M502 #5520–#5522): the albums still wanted,
|
||||
// the missing albums wanted back, or the ones held. Each open want says
|
||||
// what its last search found and how its download is getting on; Releases
|
||||
// lists every candidate with its verdict, and Search runs one now.
|
||||
// lists every candidate with its verdict, and Search runs one now. The
|
||||
// library's albums were put on the list as held once (#5567); taking one
|
||||
// off leaves its files and stops it being fetched back if they go.
|
||||
|
||||
let { list }: { list: WantState } = $props();
|
||||
|
||||
@@ -21,6 +23,7 @@
|
||||
const wantsStore = $derived(createWantsQuery(list));
|
||||
const wants = $derived($wantsStore);
|
||||
const items = $derived((wants.data?.pages ?? []).flatMap((p) => p.items) as Want[]);
|
||||
const library = $derived(wants.data?.pages[0]?.library);
|
||||
|
||||
let busy = $state<string | null>(null);
|
||||
let searching = $state<string | null>(null);
|
||||
@@ -30,7 +33,11 @@
|
||||
busy = w.id;
|
||||
try {
|
||||
await removeWant(w.id);
|
||||
pushToast(`${w.title} is off the wanted list.`);
|
||||
pushToast(
|
||||
w.held_at
|
||||
? `${w.title} is off the list. Its files stay, and it won't be fetched back if they go missing.`
|
||||
: `${w.title} is off the wanted list.`
|
||||
);
|
||||
await client.invalidateQueries({ queryKey: ['adminWants'] });
|
||||
} catch (e) {
|
||||
pushToast(errMessage(e), 'error');
|
||||
@@ -137,6 +144,7 @@
|
||||
function why(w: Want): string {
|
||||
if (w.reason === 'request') return w.requested_by ? `Requested by ${w.requested_by}` : 'Requested';
|
||||
if (w.reason === 'follow') return 'New from a followed artist';
|
||||
if (w.reason === 'library') return 'In your library when the list began';
|
||||
return 'Its files went missing';
|
||||
}
|
||||
|
||||
@@ -151,6 +159,22 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if list === 'held' && library}
|
||||
{#if !library.listed_at}
|
||||
<p class="mb-3 text-sm text-text-secondary" data-testid="library-listing">
|
||||
Your library's albums are being added to this list as held. Albums whose tags name only a
|
||||
release are looked up on MusicBrainz first, about one a second.
|
||||
</p>
|
||||
{:else if library.unlisted > 0}
|
||||
<p class="mb-3 text-sm text-text-secondary" data-testid="library-listing">
|
||||
{library.unlisted === 1 ? '1 album' : `${library.unlisted} albums`} in your library
|
||||
{library.unlisted === 1 ? "isn't" : "aren't"} on this list: nothing in {library.unlisted === 1 ? 'its' : 'their'}
|
||||
tags names {library.unlisted === 1 ? 'it' : 'them'} to MusicBrainz. Those are never fetched back if
|
||||
their files go missing.
|
||||
</p>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if wants.isPending}
|
||||
<p class="text-text-secondary">Reading the wanted list…</p>
|
||||
{:else if wants.isError && !wants.isFetchNextPageError}
|
||||
@@ -216,6 +240,17 @@
|
||||
<div class="truncate text-xs text-text-secondary" data-testid="want-arrived">{arrived(w)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if w.held_at}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => onRemove(w)}
|
||||
disabled={busy === w.id}
|
||||
aria-label="Take {w.title} off the list"
|
||||
class="inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
|
||||
>
|
||||
<X size={12} strokeWidth={2} /> Remove
|
||||
</button>
|
||||
{/if}
|
||||
{#if !w.held_at}
|
||||
<div class="flex shrink-0 flex-wrap justify-end gap-1.5">
|
||||
<button
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
||||
import { mockInfiniteQuery, mockQuery } from '../../../test-utils/query';
|
||||
import type { Want, WantGrab, WantsPage } from '#lib/api/types.js';
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/svelte";
|
||||
import { mockInfiniteQuery, mockQuery } from "../../../test-utils/query";
|
||||
import type {
|
||||
LibraryListing,
|
||||
Want,
|
||||
WantGrab,
|
||||
WantsPage,
|
||||
} from "#lib/api/types.js";
|
||||
|
||||
vi.mock('#lib/api/wanted.js', () => ({
|
||||
vi.mock("#lib/api/wanted.js", () => ({
|
||||
createWantsQuery: vi.fn(),
|
||||
removeWant: vi.fn().mockResolvedValue(undefined),
|
||||
searchWant: vi.fn().mockResolvedValue({
|
||||
outcome: 'nothing', considered: 0, best_release: '', best_verdict: '', narrowed: []
|
||||
outcome: "nothing",
|
||||
considered: 0,
|
||||
best_release: "",
|
||||
best_verdict: "",
|
||||
narrowed: [],
|
||||
}),
|
||||
createCandidatesQuery: vi.fn(),
|
||||
grabCandidate: vi.fn(),
|
||||
blockCandidate: vi.fn(),
|
||||
unblockRelease: vi.fn()
|
||||
unblockRelease: vi.fn(),
|
||||
}));
|
||||
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
|
||||
vi.mock("#lib/stores/toast.svelte.js", () => ({ pushToast: vi.fn() }));
|
||||
|
||||
import WantList from './WantList.svelte';
|
||||
import { pushToast } from '#lib/stores/toast.svelte.js';
|
||||
import { createCandidatesQuery, createWantsQuery, removeWant, searchWant } from '#lib/api/wanted.js';
|
||||
import type { WantState } from '#lib/api/types.js';
|
||||
import WantList from "./WantList.svelte";
|
||||
import { pushToast } from "#lib/stores/toast.svelte.js";
|
||||
import {
|
||||
createCandidatesQuery,
|
||||
createWantsQuery,
|
||||
removeWant,
|
||||
searchWant,
|
||||
} from "#lib/api/wanted.js";
|
||||
import type { WantState } from "#lib/api/types.js";
|
||||
|
||||
const DAY = 24 * 3_600_000;
|
||||
const ago = (ms: number) => new Date(Date.now() - ms).toISOString();
|
||||
@@ -28,172 +42,305 @@ function want(id: string, extra: Partial<Want> = {}): Want {
|
||||
return {
|
||||
id,
|
||||
release_group_mbid: `rg-${id}`,
|
||||
title: 'The Ballad of Darren',
|
||||
artist_mbid: 'blur',
|
||||
artist_name: 'Blur',
|
||||
artist_id: 'art-1',
|
||||
primary_type: 'Album',
|
||||
title: "The Ballad of Darren",
|
||||
artist_mbid: "blur",
|
||||
artist_name: "Blur",
|
||||
artist_id: "art-1",
|
||||
primary_type: "Album",
|
||||
secondary_types: [],
|
||||
year: '2023',
|
||||
reason: 'follow',
|
||||
year: "2023",
|
||||
reason: "follow",
|
||||
wanted_at: ago(2 * DAY),
|
||||
held_at: null,
|
||||
image_url: '',
|
||||
...extra
|
||||
image_url: "",
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
function page(items: Want[]): WantsPage {
|
||||
function page(
|
||||
items: Want[],
|
||||
library: LibraryListing = { listed_at: ago(DAY), unlisted: 0 },
|
||||
): WantsPage {
|
||||
return {
|
||||
items,
|
||||
total: items.length,
|
||||
limit: 50,
|
||||
offset: 0,
|
||||
counts: { wanted: 2, missing: 1, held: 4, following: 1, activity: 1, attention: 0, blocked: 0 }
|
||||
counts: {
|
||||
wanted: 2,
|
||||
missing: 1,
|
||||
held: 4,
|
||||
following: 1,
|
||||
activity: 1,
|
||||
attention: 0,
|
||||
blocked: 0,
|
||||
},
|
||||
library,
|
||||
};
|
||||
}
|
||||
|
||||
function grab(extra: Partial<WantGrab> = {}): WantGrab {
|
||||
return {
|
||||
id: 'g1',
|
||||
name: 'Blur - Blur (1997) [FLAC]',
|
||||
id: "g1",
|
||||
name: "Blur - Blur (1997) [FLAC]",
|
||||
grabbed_at: ago(60_000),
|
||||
origin: 'schedule',
|
||||
quality: 'lossless',
|
||||
origin: "schedule",
|
||||
quality: "lossless",
|
||||
live: true,
|
||||
end_reason: '',
|
||||
end_reason: "",
|
||||
progress: 0,
|
||||
import_state: '',
|
||||
import_reason: '',
|
||||
end_detail: '',
|
||||
import_state: "",
|
||||
import_reason: "",
|
||||
end_detail: "",
|
||||
imported_at: null,
|
||||
imported_files: 0,
|
||||
...extra
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
function setup(
|
||||
items: Want[] = [want('w1'), want('w2', { reason: 'request', requested_by: 'sam', title: 'Parklife' })],
|
||||
state: WantState = 'wanted'
|
||||
items: Want[] = [
|
||||
want("w1"),
|
||||
want("w2", { reason: "request", requested_by: "sam", title: "Parklife" }),
|
||||
],
|
||||
state: WantState = "wanted",
|
||||
library?: LibraryListing,
|
||||
) {
|
||||
vi.mocked(createWantsQuery).mockReturnValue(
|
||||
mockInfiniteQuery({ pages: [page(items)] }) as unknown as ReturnType<typeof createWantsQuery>
|
||||
mockInfiniteQuery({
|
||||
pages: [page(items, library)],
|
||||
}) as unknown as ReturnType<typeof createWantsQuery>,
|
||||
);
|
||||
vi.mocked(createCandidatesQuery).mockReturnValue(
|
||||
mockQuery({ data: [] }) as unknown as ReturnType<typeof createCandidatesQuery>
|
||||
mockQuery({ data: [] }) as unknown as ReturnType<
|
||||
typeof createCandidatesQuery
|
||||
>,
|
||||
);
|
||||
return render(WantList, { list: state });
|
||||
}
|
||||
|
||||
describe('WantList', () => {
|
||||
test('asks for its own state', () => {
|
||||
setup([], 'missing');
|
||||
expect(createWantsQuery).toHaveBeenCalledWith('missing');
|
||||
describe("WantList", () => {
|
||||
test("asks for its own state", () => {
|
||||
setup([], "missing");
|
||||
expect(createWantsQuery).toHaveBeenCalledWith("missing");
|
||||
expect(screen.getByText(/no missing albums are wanted back/i)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('a want says why it is wanted', () => {
|
||||
test("a want says why it is wanted", () => {
|
||||
setup();
|
||||
const why = screen.getAllByTestId('want-why').map((e) => e.textContent ?? '');
|
||||
const why = screen
|
||||
.getAllByTestId("want-why")
|
||||
.map((e) => e.textContent ?? "");
|
||||
expect(why[0]).toMatch(/new from a followed artist/i);
|
||||
expect(why[1]).toMatch(/requested by sam/i);
|
||||
});
|
||||
|
||||
test('remove takes a want off the list', async () => {
|
||||
test("remove takes a want off the list", async () => {
|
||||
setup();
|
||||
await fireEvent.click(screen.getByRole('button', { name: /stop wanting parklife/i }));
|
||||
await waitFor(() => expect(removeWant).toHaveBeenCalledWith('w2'));
|
||||
await fireEvent.click(
|
||||
screen.getByRole("button", { name: /stop wanting parklife/i }),
|
||||
);
|
||||
await waitFor(() => expect(removeWant).toHaveBeenCalledWith("w2"));
|
||||
});
|
||||
|
||||
test('a held want has no remove button', () => {
|
||||
setup([want('w1', { held_at: ago(DAY), album_id: 'al-1' })]);
|
||||
expect(screen.queryByRole('button', { name: /stop wanting/i })).toBeNull();
|
||||
expect(screen.getByRole('link', { name: /the ballad of darren/i }).getAttribute('href')).toBe('/albums/al-1');
|
||||
test("a held album can be taken off the list, and is not searched for", async () => {
|
||||
setup(
|
||||
[want("w1", { held_at: ago(DAY), album_id: "al-1", reason: "library" })],
|
||||
"held",
|
||||
);
|
||||
expect(
|
||||
screen.queryByRole("button", { name: /stop wanting|search for/i }),
|
||||
).toBeNull();
|
||||
expect(
|
||||
screen
|
||||
.getByRole("link", { name: /the ballad of darren/i })
|
||||
.getAttribute("href"),
|
||||
).toBe("/albums/al-1");
|
||||
expect(screen.getByTestId("want-why").textContent).toMatch(
|
||||
/in your library when the list began/i,
|
||||
);
|
||||
await fireEvent.click(
|
||||
screen.getByRole("button", {
|
||||
name: /take the ballad of darren off the list/i,
|
||||
}),
|
||||
);
|
||||
await waitFor(() => expect(removeWant).toHaveBeenCalledWith("w1"));
|
||||
});
|
||||
|
||||
test('an empty list explains what lands there', () => {
|
||||
test("the held tab says while the library is being listed, and what could not be", () => {
|
||||
setup([], "held", { listed_at: null, unlisted: 0 });
|
||||
expect(screen.getByTestId("library-listing").textContent).toMatch(
|
||||
/being added to this list/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("the held tab names how many albums nothing names to MusicBrainz", () => {
|
||||
setup([want("w1", { held_at: ago(DAY) })], "held", {
|
||||
listed_at: ago(DAY),
|
||||
unlisted: 12,
|
||||
});
|
||||
expect(screen.getByTestId("library-listing").textContent).toMatch(
|
||||
/12 albums in your library\s+aren't on this list/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("an empty list explains what lands there", () => {
|
||||
setup([]);
|
||||
expect(screen.getByText(/nothing is wanted/i)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('each open want says what its last search found', () => {
|
||||
test("each open want says what its last search found", () => {
|
||||
setup([
|
||||
want('w1'),
|
||||
want('w2', {
|
||||
title: 'Parklife',
|
||||
search: { searched_at: ago(3_600_000), outcome: 'nothing', considered: 4, best_release: 'x', best_verdict: 'rejected', error: '' }
|
||||
want("w1"),
|
||||
want("w2", {
|
||||
title: "Parklife",
|
||||
search: {
|
||||
searched_at: ago(3_600_000),
|
||||
outcome: "nothing",
|
||||
considered: 4,
|
||||
best_release: "x",
|
||||
best_verdict: "rejected",
|
||||
error: "",
|
||||
},
|
||||
}),
|
||||
want("w3", { title: "Blur", grab: grab() }),
|
||||
want("w4", {
|
||||
title: "13",
|
||||
grab: grab({ import_state: "downloading", progress: 0.42 }),
|
||||
}),
|
||||
want('w3', { title: 'Blur', grab: grab() }),
|
||||
want('w4', { title: '13', grab: grab({ import_state: 'downloading', progress: 0.42 }) })
|
||||
]);
|
||||
const lines = screen.getAllByTestId('want-search').map((e) => e.textContent ?? '');
|
||||
const lines = screen
|
||||
.getAllByTestId("want-search")
|
||||
.map((e) => e.textContent ?? "");
|
||||
expect(lines[0]).toMatch(/not searched yet/i);
|
||||
expect(lines[1]).toMatch(/4 found, none of them this album/i);
|
||||
expect(lines[2]).toMatch(/downloading blur - blur \(1997\) \[flac\], taken by the schedule/i);
|
||||
expect(lines[2]).toMatch(
|
||||
/downloading blur - blur \(1997\) \[flac\], taken by the schedule/i,
|
||||
);
|
||||
expect(lines[3]).toMatch(/downloading 42%/i);
|
||||
});
|
||||
|
||||
test('a download waiting on a person says why and points to Activity', () => {
|
||||
test("a download waiting on a person says why and points to Activity", () => {
|
||||
setup([
|
||||
want('w1', { grab: grab({ import_state: 'review', import_reason: 'Only the lengths agree. A person decides.' }) })
|
||||
want("w1", {
|
||||
grab: grab({
|
||||
import_state: "review",
|
||||
import_reason: "Only the lengths agree. A person decides.",
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
const line = screen.getByTestId('want-search');
|
||||
const line = screen.getByTestId("want-search");
|
||||
expect(line.textContent).toMatch(/needs you: only the lengths agree/i);
|
||||
expect(screen.getByRole('link', { name: /decide on activity/i }).getAttribute('href')).toBe('/wanted/activity');
|
||||
expect(
|
||||
screen
|
||||
.getByRole("link", { name: /decide on activity/i })
|
||||
.getAttribute("href"),
|
||||
).toBe("/wanted/activity");
|
||||
// A live download can't be searched for again.
|
||||
expect((screen.getByRole('button', { name: /search for the ballad of darren now/i }) as HTMLButtonElement).disabled).toBe(true);
|
||||
expect(
|
||||
(
|
||||
screen.getByRole("button", {
|
||||
name: /search for the ballad of darren now/i,
|
||||
}) as HTMLButtonElement
|
||||
).disabled,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('a refused download is named beside the search that follows it', () => {
|
||||
test("a refused download is named beside the search that follows it", () => {
|
||||
setup([
|
||||
want('w1', {
|
||||
search: { searched_at: ago(3_600_000), outcome: 'nothing', considered: 0, best_release: '', best_verdict: '', error: '' },
|
||||
grab: grab({ live: false, end_reason: 'refused', import_state: 'refused', import_reason: 'Another album.' })
|
||||
})
|
||||
want("w1", {
|
||||
search: {
|
||||
searched_at: ago(3_600_000),
|
||||
outcome: "nothing",
|
||||
considered: 0,
|
||||
best_release: "",
|
||||
best_verdict: "",
|
||||
error: "",
|
||||
},
|
||||
grab: grab({
|
||||
live: false,
|
||||
end_reason: "refused",
|
||||
import_state: "refused",
|
||||
import_reason: "Another album.",
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
expect(screen.getByTestId('want-search').textContent).toMatch(/last download refused: another album/i);
|
||||
expect(screen.getByTestId("want-search").textContent).toMatch(
|
||||
/last download refused: another album/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('a download given up on is named beside the search that follows it', () => {
|
||||
test("a download given up on is named beside the search that follows it", () => {
|
||||
setup([
|
||||
want('w1', {
|
||||
search: { searched_at: ago(3_600_000), outcome: 'waiting', considered: 4, best_release: '', best_verdict: '', error: '' },
|
||||
grab: grab({ live: false, end_reason: 'stalled', import_state: 'downloading', end_detail: 'Nobody was sharing it.' })
|
||||
})
|
||||
want("w1", {
|
||||
search: {
|
||||
searched_at: ago(3_600_000),
|
||||
outcome: "waiting",
|
||||
considered: 4,
|
||||
best_release: "",
|
||||
best_verdict: "",
|
||||
error: "",
|
||||
},
|
||||
grab: grab({
|
||||
live: false,
|
||||
end_reason: "stalled",
|
||||
import_state: "downloading",
|
||||
end_detail: "Nobody was sharing it.",
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
expect(screen.getByTestId('want-search').textContent).toMatch(/last download given up: nobody was sharing it/i);
|
||||
expect(screen.getByTestId("want-search").textContent).toMatch(
|
||||
/last download given up: nobody was sharing it/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('a held album says how it arrived', () => {
|
||||
test("a held album says how it arrived", () => {
|
||||
setup(
|
||||
[
|
||||
want('w1', {
|
||||
want("w1", {
|
||||
held_at: ago(DAY),
|
||||
album_id: 'al-1',
|
||||
grab: grab({ live: false, end_reason: 'imported', import_state: 'imported', imported_at: ago(DAY), imported_files: 11 })
|
||||
})
|
||||
album_id: "al-1",
|
||||
grab: grab({
|
||||
live: false,
|
||||
end_reason: "imported",
|
||||
import_state: "imported",
|
||||
imported_at: ago(DAY),
|
||||
imported_files: 11,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
'held'
|
||||
"held",
|
||||
);
|
||||
expect(screen.getByTestId("want-arrived").textContent).toMatch(
|
||||
/imported 11 tracks .* from blur - blur/i,
|
||||
);
|
||||
expect(screen.getByTestId('want-arrived').textContent).toMatch(/imported 11 tracks .* from blur - blur/i);
|
||||
});
|
||||
|
||||
test('Search runs one now and says what came of it', async () => {
|
||||
test("Search runs one now and says what came of it", async () => {
|
||||
setup();
|
||||
await fireEvent.click(screen.getByRole('button', { name: /search for parklife now/i }));
|
||||
await waitFor(() => expect(searchWant).toHaveBeenCalledWith('w2'));
|
||||
await waitFor(() => expect(pushToast).toHaveBeenCalledWith('Nothing found for Parklife.', 'info'));
|
||||
await fireEvent.click(
|
||||
screen.getByRole("button", { name: /search for parklife now/i }),
|
||||
);
|
||||
await waitFor(() => expect(searchWant).toHaveBeenCalledWith("w2"));
|
||||
await waitFor(() =>
|
||||
expect(pushToast).toHaveBeenCalledWith(
|
||||
"Nothing found for Parklife.",
|
||||
"info",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('Releases opens what the last search found', async () => {
|
||||
test("Releases opens what the last search found", async () => {
|
||||
setup();
|
||||
expect(screen.queryByTestId('want-candidates')).toBeNull();
|
||||
const toggle = screen.getByRole('button', { name: /releases found for parklife/i });
|
||||
expect(screen.queryByTestId("want-candidates")).toBeNull();
|
||||
const toggle = screen.getByRole("button", {
|
||||
name: /releases found for parklife/i,
|
||||
});
|
||||
await fireEvent.click(toggle);
|
||||
expect(toggle.getAttribute('aria-expanded')).toBe('true');
|
||||
expect(screen.getByTestId('want-candidates')).toBeTruthy();
|
||||
expect(createCandidatesQuery).toHaveBeenCalledWith('w2');
|
||||
expect(toggle.getAttribute("aria-expanded")).toBe("true");
|
||||
expect(screen.getByTestId("want-candidates")).toBeTruthy();
|
||||
expect(createCandidatesQuery).toHaveBeenCalledWith("w2");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user