Skip to content

Commit 1ce26a8

Browse files
committed
finish tests
1 parent 08f7983 commit 1ce26a8

File tree

9 files changed

+327
-27
lines changed

9 files changed

+327
-27
lines changed

exercises/03.complex/04.solution.dynamic-sizing/test/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { z } from 'zod'
88
const mcpServerPort = inject('mcpServerPort')
99

1010
async function setupBrowser() {
11-
const browser = await chromium.launch({ headless: false })
11+
const browser = await chromium.launch({ headless: true })
1212
const page = await browser.newPage()
1313
return {
1414
browser,

exercises/04.interactive/02.problem.tools/test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ test('journal viewer sends tool message', async () => {
7474
await viewDetailsButton.click()
7575

7676
const message = page.getByRole('log').getByText('tool')
77-
await message.waitFor({ timeout: 10_000 }).catch((e) => {
77+
await message.waitFor({ timeout: 1000 }).catch((e) => {
7878
throw new Error(
7979
'🚨 tool message was never received. Make sure to call sendMcpMessage with "tool"',
8080
{ cause: e },
@@ -97,7 +97,7 @@ test('journal viewer sends tool message', async () => {
9797
// then click the "send" button
9898
// no need to input anything in this case because there's no expected response
9999
await page.getByRole('button', { name: 'send' }).click()
100-
}, 50_000)
100+
})
101101

102102
// because vite needs to optimize deps 😭😡
103103
async function handleViteDeps(page: Page) {

exercises/04.interactive/02.solution.tools/test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ test('journal viewer sends tool message', async () => {
7474
await viewDetailsButton.click()
7575

7676
const message = page.getByRole('log').getByText('tool')
77-
await message.waitFor({ timeout: 10_000 }).catch((e) => {
77+
await message.waitFor({ timeout: 1000 }).catch((e) => {
7878
throw new Error(
7979
'🚨 tool message was never received. Make sure to call sendMcpMessage with "tool"',
8080
{ cause: e },
@@ -97,7 +97,7 @@ test('journal viewer sends tool message', async () => {
9797
// then click the "send" button
9898
// no need to input anything in this case because there's no expected response
9999
await page.getByRole('button', { name: 'send' }).click()
100-
}, 50_000)
100+
})
101101

102102
// because vite needs to optimize deps 😭😡
103103
async function handleViteDeps(page: Page) {

exercises/04.interactive/03.problem.prompts/test/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { z } from 'zod'
88
const mcpServerPort = inject('mcpServerPort')
99

1010
async function setupBrowser() {
11-
const browser = await chromium.launch({ headless: false })
11+
const browser = await chromium.launch({ headless: true })
1212
const page = await browser.newPage()
1313
return {
1414
browser,
@@ -74,7 +74,7 @@ test('journal viewer sends prompt message', async () => {
7474
await viewDetailsButton.click()
7575

7676
const message = page.getByRole('log').getByText('prompt')
77-
await message.waitFor({ timeout: 10_000 }).catch((e) => {
77+
await message.waitFor({ timeout: 1000 }).catch((e) => {
7878
throw new Error(
7979
'🚨 prompt message was never received. Make sure to call sendMcpMessage with "prompt"',
8080
{ cause: e },
@@ -96,7 +96,7 @@ test('journal viewer sends prompt message', async () => {
9696
// then click the "send" button
9797
// no need to input anything in this case because there's no expected response
9898
await page.getByRole('button', { name: 'send' }).click()
99-
}, 50_000)
99+
})
100100

101101
// because vite needs to optimize deps 😭😡
102102
async function handleViteDeps(page: Page) {

exercises/04.interactive/03.solution.prompts/test/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { z } from 'zod'
88
const mcpServerPort = inject('mcpServerPort')
99

1010
async function setupBrowser() {
11-
const browser = await chromium.launch({ headless: false })
11+
const browser = await chromium.launch({ headless: true })
1212
const page = await browser.newPage()
1313
return {
1414
browser,
@@ -74,7 +74,7 @@ test('journal viewer sends prompt message', async () => {
7474
await viewDetailsButton.click()
7575

7676
const message = page.getByRole('log').getByText('prompt')
77-
await message.waitFor({ timeout: 10_000 }).catch((e) => {
77+
await message.waitFor({ timeout: 1000 }).catch((e) => {
7878
throw new Error(
7979
'🚨 prompt message was never received. Make sure to call sendMcpMessage with "prompt"',
8080
{ cause: e },
@@ -96,7 +96,7 @@ test('journal viewer sends prompt message', async () => {
9696
// then click the "send" button
9797
// no need to input anything in this case because there's no expected response
9898
await page.getByRole('button', { name: 'send' }).click()
99-
}, 50_000)
99+
})
100100

101101
// because vite needs to optimize deps 😭😡
102102
async function handleViteDeps(page: Page) {

exercises/05.advanced/01.problem.tool-results/test/index.test.ts

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1-
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
1+
import { invariant } from '@epic-web/invariant'
2+
import { Client } from '@modelcontextprotocol/sdk/client'
23
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
4+
import { chromium, type Page } from 'playwright'
35
import { test, expect, inject } from 'vitest'
6+
import { z } from 'zod'
47

58
const mcpServerPort = inject('mcpServerPort')
69

10+
async function setupBrowser() {
11+
const browser = await chromium.launch({ headless: true })
12+
const page = await browser.newPage()
13+
return {
14+
browser,
15+
page,
16+
async [Symbol.asyncDispose]() {
17+
await browser.close()
18+
},
19+
}
20+
}
21+
722
async function setupClient() {
823
const client = new Client(
924
{
@@ -27,10 +42,88 @@ async function setupClient() {
2742
}
2843
}
2944

30-
test('listing tools works', async () => {
45+
test('journal viewer sends tool message', async () => {
3146
await using setup = await setupClient()
3247
const { client } = setup
3348

34-
const result = await client.listTools()
35-
expect(result.tools.length).toBeGreaterThan(0)
49+
const result = await client.callTool({ name: 'view_journal' }).catch((e) => {
50+
throw new Error('🚨 view_journal tool call failed', { cause: e })
51+
})
52+
53+
invariant(Array.isArray(result.content), '🚨 content is not an array')
54+
55+
const { resource } = z
56+
.object({ resource: z.object({}).passthrough() })
57+
.parse(result.content[0])
58+
59+
const url = new URL('http://localhost:7787/mcp-ui-renderer')
60+
url.searchParams.set('resourceData', JSON.stringify(resource))
61+
62+
await using browserSetup = await setupBrowser()
63+
const { page } = browserSetup
64+
65+
await page.goto(url.toString())
66+
67+
await handleViteDeps(page)
68+
69+
const iframe = page.frameLocator('iframe')
70+
71+
const deleteEntryButton = iframe
72+
.getByRole('button', { name: 'Delete' })
73+
.first()
74+
await deleteEntryButton.click()
75+
await iframe.getByRole('button', { name: 'Confirm?' }).click()
76+
77+
const message = page.getByRole('log').getByText('tool')
78+
await message.waitFor({ timeout: 1000 }).catch((e) => {
79+
throw new Error(
80+
'🚨 tool message was never received. Make sure to call sendMcpMessage with "tool"',
81+
{ cause: e },
82+
)
83+
})
84+
85+
const textContent = await message.textContent()
86+
const messageContent = JSON.parse(textContent!)
87+
expect(
88+
messageContent,
89+
'🚨 the tool message is not the correct format',
90+
).toEqual({
91+
type: 'tool',
92+
messageId: expect.any(String),
93+
payload: {
94+
toolName: 'delete_entry',
95+
params: { id: expect.any(Number) },
96+
},
97+
})
98+
// then click the "send" button
99+
// no need to input anything in this case because there's no expected response
100+
await page
101+
.getByRole('textbox', { name: /message input/i })
102+
.fill('{"structuredContent": {"success": true}}')
103+
await page.getByRole('button', { name: 'send' }).click()
104+
await iframe
105+
.getByText('Deleted')
106+
.waitFor({ timeout: 1000 })
107+
.catch((e) => {
108+
throw new Error('🚨 delete_entry response was not processed properly', {
109+
cause: e,
110+
})
111+
})
36112
})
113+
114+
// because vite needs to optimize deps 😭😡
115+
async function handleViteDeps(page: Page) {
116+
await page
117+
.frameLocator('iframe')
118+
.locator('vite-error-overlay')
119+
.waitFor({ timeout: 200 })
120+
.then(
121+
async () => {
122+
await page.reload()
123+
await new Promise((resolve) => setTimeout(resolve, 400))
124+
},
125+
() => {
126+
// good...
127+
},
128+
)
129+
}

exercises/05.advanced/01.solution.tool-results/test/index.test.ts

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1-
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
1+
import { invariant } from '@epic-web/invariant'
2+
import { Client } from '@modelcontextprotocol/sdk/client'
23
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
4+
import { chromium, type Page } from 'playwright'
35
import { test, expect, inject } from 'vitest'
6+
import { z } from 'zod'
47

58
const mcpServerPort = inject('mcpServerPort')
69

10+
async function setupBrowser() {
11+
const browser = await chromium.launch({ headless: true })
12+
const page = await browser.newPage()
13+
return {
14+
browser,
15+
page,
16+
async [Symbol.asyncDispose]() {
17+
await browser.close()
18+
},
19+
}
20+
}
21+
722
async function setupClient() {
823
const client = new Client(
924
{
@@ -27,10 +42,88 @@ async function setupClient() {
2742
}
2843
}
2944

30-
test('listing tools works', async () => {
45+
test('journal viewer sends tool message', async () => {
3146
await using setup = await setupClient()
3247
const { client } = setup
3348

34-
const result = await client.listTools()
35-
expect(result.tools.length).toBeGreaterThan(0)
49+
const result = await client.callTool({ name: 'view_journal' }).catch((e) => {
50+
throw new Error('🚨 view_journal tool call failed', { cause: e })
51+
})
52+
53+
invariant(Array.isArray(result.content), '🚨 content is not an array')
54+
55+
const { resource } = z
56+
.object({ resource: z.object({}).passthrough() })
57+
.parse(result.content[0])
58+
59+
const url = new URL('http://localhost:7787/mcp-ui-renderer')
60+
url.searchParams.set('resourceData', JSON.stringify(resource))
61+
62+
await using browserSetup = await setupBrowser()
63+
const { page } = browserSetup
64+
65+
await page.goto(url.toString())
66+
67+
await handleViteDeps(page)
68+
69+
const iframe = page.frameLocator('iframe')
70+
71+
const deleteEntryButton = iframe
72+
.getByRole('button', { name: 'Delete' })
73+
.first()
74+
await deleteEntryButton.click()
75+
await iframe.getByRole('button', { name: 'Confirm?' }).click()
76+
77+
const message = page.getByRole('log').getByText('tool')
78+
await message.waitFor({ timeout: 1000 }).catch((e) => {
79+
throw new Error(
80+
'🚨 tool message was never received. Make sure to call sendMcpMessage with "tool"',
81+
{ cause: e },
82+
)
83+
})
84+
85+
const textContent = await message.textContent()
86+
const messageContent = JSON.parse(textContent!)
87+
expect(
88+
messageContent,
89+
'🚨 the tool message is not the correct format',
90+
).toEqual({
91+
type: 'tool',
92+
messageId: expect.any(String),
93+
payload: {
94+
toolName: 'delete_entry',
95+
params: { id: expect.any(Number) },
96+
},
97+
})
98+
// then click the "send" button
99+
// no need to input anything in this case because there's no expected response
100+
await page
101+
.getByRole('textbox', { name: /message input/i })
102+
.fill('{"structuredContent": {"success": true}}')
103+
await page.getByRole('button', { name: 'send' }).click()
104+
await iframe
105+
.getByText('Deleted')
106+
.waitFor({ timeout: 1000 })
107+
.catch((e) => {
108+
throw new Error('🚨 delete_entry response was not processed properly', {
109+
cause: e,
110+
})
111+
})
36112
})
113+
114+
// because vite needs to optimize deps 😭😡
115+
async function handleViteDeps(page: Page) {
116+
await page
117+
.frameLocator('iframe')
118+
.locator('vite-error-overlay')
119+
.waitFor({ timeout: 200 })
120+
.then(
121+
async () => {
122+
await page.reload()
123+
await new Promise((resolve) => setTimeout(resolve, 400))
124+
},
125+
() => {
126+
// good...
127+
},
128+
)
129+
}

0 commit comments

Comments
 (0)