Skip to content
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 @@ -66,6 +66,44 @@ describe('injectInfiniteQuery', () => {
).toBeInTheDocument()
})

it('should reject and update signal', async () => {
const key = queryKey()

@Component({
template: `
<div>status: {{ query.status() }}</div>
<div>pages: {{ query.data()?.pages?.join(', ') ?? 'none' }}</div>
<div>error: {{ query.error()?.message ?? 'none' }}</div>
<div>isError: {{ query.isError() }}</div>
<div>failureCount: {{ query.failureCount() }}</div>
`,
})
class Page {
readonly query = injectInfiniteQuery(() => ({
retry: false,
queryKey: key,
queryFn: () =>
sleep(10).then(() => Promise.reject(new Error('Some error'))),
initialPageParam: 0,
getNextPageParam: () => 12,
}))
}

const rendered = await render(Page)

expect(rendered.getByText('status: pending')).toBeInTheDocument()
expect(rendered.getByText('pages: none')).toBeInTheDocument()

await vi.advanceTimersByTimeAsync(11)
rendered.fixture.detectChanges()

expect(rendered.getByText('status: error')).toBeInTheDocument()
expect(rendered.getByText('pages: none')).toBeInTheDocument()
expect(rendered.getByText('error: Some error')).toBeInTheDocument()
expect(rendered.getByText('isError: true')).toBeInTheDocument()
expect(rendered.getByText('failureCount: 1')).toBeInTheDocument()
})

describe('injection context', () => {
it('should throw NG0203 with descriptive error outside injection context', () => {
const key = queryKey()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,47 +50,67 @@ describe('injectMutation', () => {
it('should change state after invoking mutate', async () => {
const result = 'Mock data'

const mutation = TestBed.runInInjectionContext(() => {
return injectMutation(() => ({
@Component({
template: `
<div>isIdle: {{ mutation.isIdle() }}</div>
<div>isPending: {{ mutation.isPending() }}</div>
<div>isError: {{ mutation.isError() }}</div>
<div>isSuccess: {{ mutation.isSuccess() }}</div>
<div>data: {{ mutation.data() ?? 'none' }}</div>
<div>error: {{ mutation.error()?.message ?? 'none' }}</div>
`,
})
class Page {
readonly mutation = injectMutation(() => ({
mutationFn: (params: string) => sleep(10).then(() => params),
}))
})
}

TestBed.tick()
const rendered = await render(Page)

mutation.mutate(result)
rendered.fixture.componentInstance.mutation.mutate(result)
await vi.advanceTimersByTimeAsync(0)
rendered.fixture.detectChanges()

expectSignals(mutation, {
isIdle: false,
isPending: true,
isError: false,
isSuccess: false,
data: undefined,
error: null,
})
expect(rendered.getByText('isIdle: false')).toBeInTheDocument()
expect(rendered.getByText('isPending: true')).toBeInTheDocument()
expect(rendered.getByText('isError: false')).toBeInTheDocument()
expect(rendered.getByText('isSuccess: false')).toBeInTheDocument()
expect(rendered.getByText('data: none')).toBeInTheDocument()
expect(rendered.getByText('error: none')).toBeInTheDocument()
})

it('should return error when request fails', async () => {
const mutation = TestBed.runInInjectionContext(() => {
return injectMutation(() => ({
@Component({
template: `
<div>isIdle: {{ mutation.isIdle() }}</div>
<div>isPending: {{ mutation.isPending() }}</div>
<div>isError: {{ mutation.isError() }}</div>
<div>isSuccess: {{ mutation.isSuccess() }}</div>
<div>data: {{ mutation.data() ?? 'none' }}</div>
<div>error: {{ mutation.error()?.message ?? 'none' }}</div>
`,
})
class Page {
readonly mutation = injectMutation(() => ({
mutationFn: () =>
sleep(10).then(() => Promise.reject(new Error('Some error'))),
}))
})
}

mutation.mutate()
const rendered = await render(Page)

rendered.fixture.componentInstance.mutation.mutate()

await vi.advanceTimersByTimeAsync(11)
rendered.fixture.detectChanges()

expectSignals(mutation, {
isIdle: false,
isPending: false,
isError: true,
isSuccess: false,
data: undefined,
error: Error('Some error'),
})
expect(rendered.getByText('isIdle: false')).toBeInTheDocument()
expect(rendered.getByText('isPending: false')).toBeInTheDocument()
expect(rendered.getByText('isError: true')).toBeInTheDocument()
expect(rendered.getByText('isSuccess: false')).toBeInTheDocument()
expect(rendered.getByText('data: none')).toBeInTheDocument()
expect(rendered.getByText('error: Some error')).toBeInTheDocument()
})

it('should return data when request succeeds', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,62 @@ describe('injectQuery', () => {
expect(rendered.getByText('failureReason: Some error')).toBeInTheDocument()
})

it('should be able to select a part of the data with select', async () => {
const key = queryKey()

@Component({
template: `<div>data: {{ query.data() ?? 'none' }}</div>`,
})
class Page {
readonly query = injectQuery<{ name: string }, Error, string>(() => ({
queryKey: key,
queryFn: () => sleep(10).then(() => ({ name: 'test' })),
select: (data) => data.name,
}))
}

const rendered = await render(Page)

expect(rendered.getByText('data: none')).toBeInTheDocument()

await vi.advanceTimersByTimeAsync(11)
rendered.fixture.detectChanges()

expect(rendered.getByText('data: test')).toBeInTheDocument()
})

it('should show placeholderData until queryFn resolves and then expose real data', async () => {
const key = queryKey()

@Component({
template: `
<div>data: {{ query.data() }}</div>
<div>isPlaceholderData: {{ query.isPlaceholderData() }}</div>
<div>isSuccess: {{ query.isSuccess() }}</div>
`,
})
class Page {
readonly query = injectQuery(() => ({
queryKey: key,
queryFn: () => sleep(10).then(() => 'real-data'),
placeholderData: 'placeholder',
}))
}

const rendered = await render(Page)

expect(rendered.getByText('data: placeholder')).toBeInTheDocument()
expect(rendered.getByText('isPlaceholderData: true')).toBeInTheDocument()
expect(rendered.getByText('isSuccess: true')).toBeInTheDocument()

await vi.advanceTimersByTimeAsync(11)
rendered.fixture.detectChanges()

expect(rendered.getByText('data: real-data')).toBeInTheDocument()
expect(rendered.getByText('isPlaceholderData: false')).toBeInTheDocument()
expect(rendered.getByText('isSuccess: true')).toBeInTheDocument()
})

it('should update query on options contained signal change', async () => {
const key1 = queryKey()
const key2 = queryKey()
Expand Down Expand Up @@ -548,24 +604,6 @@ describe('injectQuery', () => {
})
})

it('should set state to error when queryFn returns reject promise', async () => {
const key = queryKey()
const query = TestBed.runInInjectionContext(() => {
return injectQuery(() => ({
retry: false,
queryKey: key,
queryFn: () =>
sleep(10).then(() => Promise.reject(new Error('Some error'))),
}))
})

expect(query.status()).toBe('pending')

await vi.advanceTimersByTimeAsync(11)

expect(query.status()).toBe('error')
})

it('should render with required signal inputs', async () => {
@Component({
selector: 'app-fake',
Expand Down
Loading