mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 04:23:15 -05:00
Token help now works: a button opens GitHub's fine-grained token page, the steps are open while there is no connection, and a table lists the permissions for read only and for write modes. The page says it does not answer mentions yet. Errors sit under their own field. The repository list splits on lines and commas only, so "not a repo" is one entry and is named in the message. The connection name defaults to the first repository. The write mode cards each carry one consequence line, and "Allowed" is now "Acts without asking". The Test result is drawn per repository in the row, with a tone, a busy line, and a plain text when GitHub refuses the token. The unit tests and a browser spec cover the page. Assisted-by: Claude:claude-sonnet-5-5 node playwright eslint
95 lines
5.3 KiB
JavaScript
95 lines
5.3 KiB
JavaScript
import { test, expect } from './coverage-fixtures.js'
|
|
import { mockChat } from './chat-fixtures.js'
|
|
import { installStudioAddons } from './studio-addon.js'
|
|
import { connectorApi } from './connector-fixtures.js'
|
|
|
|
// The page of the GitHub add-on (the real built bundle, inside the add-on
|
|
// shell) with the core API stubbed: the token help, the repository list and
|
|
// its errors, the write mode cards, and the Test result drawn in the row.
|
|
|
|
const PAGE = '/app/addons/github/apps'
|
|
const BINDING = { id: 'b1', name: 'Docs repo', kind: 'github', agent_id: 'a1', enabled: false, accept_any: false, allow: [], resources: ['acme/widgets', 'acme/docs'], write_mode: 'propose', has_token: true }
|
|
const KIND = { capabilities: [], kindExtra: { title: 'GitHub', files: undefined } }
|
|
const api = (page, initial = []) => connectorApi(page, initial, 'github', [], undefined, KIND)
|
|
|
|
// The check tool answers through the add-on gateway of the page.
|
|
const checkAnswer = (answer) => (body) => (body.arguments?.tool === 'check'
|
|
? { status: 200, contentType: 'application/json', body: JSON.stringify({ payload: { data: answer(body.arguments.repo) } }) }
|
|
: null)
|
|
|
|
test.describe('GitHub add-on', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await mockChat(page)
|
|
})
|
|
const install = (page, answer = () => ({ reachable: true })) => installStudioAddons(page, { ids: ['github'], dispatch: checkAnswer(answer) })
|
|
|
|
test('a first connection sees the token help open, a link to GitHub and the permission table', async ({ page }) => {
|
|
await install(page)
|
|
await api(page).install()
|
|
await page.goto(PAGE)
|
|
await expect(page.getByTestId('github-limits')).toContainText('does not answer @mentions')
|
|
await expect(page.getByTestId('github-setup')).toHaveAttribute('open', '')
|
|
const link = page.getByTestId('github-token-link').first()
|
|
await expect(link).toHaveAttribute('href', 'https://github.com/settings/personal-access-tokens/new')
|
|
await expect(link).toHaveAttribute('target', '_blank')
|
|
await expect(page.getByTestId('github-perms')).toContainText('Pull requests')
|
|
await expect(page.getByTestId('github-perms')).toContainText('Read and write')
|
|
})
|
|
|
|
test('with a connection the help is closed', async ({ page }) => {
|
|
await install(page)
|
|
await api(page, [BINDING]).install()
|
|
await page.goto(PAGE)
|
|
await expect(page.getByTestId('connector-row')).toContainText('Docs repo')
|
|
await expect(page.getByTestId('github-setup')).not.toHaveAttribute('open', '')
|
|
})
|
|
|
|
test('errors sit under their field, the bad line is named and the name follows the first repository', async ({ page }) => {
|
|
await install(page)
|
|
await api(page).install()
|
|
await page.goto(PAGE)
|
|
await page.getByRole('button', { name: 'New GitHub connection' }).click()
|
|
await page.getByLabel('Fine-grained access token').fill('ghp_classic')
|
|
await expect(page.getByLabel('Fine-grained access token')).toHaveAttribute('aria-invalid', 'true')
|
|
await expect(page.getByLabel('Fine-grained access token')).toHaveAccessibleDescription(/starts with github_pat_/)
|
|
await page.getByLabel('Repositories the agent may use').fill('acme/widgets\nnot a repo')
|
|
await expect(page.getByLabel('Repositories the agent may use')).toHaveAccessibleDescription(/"not a repo" is not written owner\/name/)
|
|
await expect(page.getByLabel('Name', { exact: true })).toHaveValue('GitHub: acme/widgets')
|
|
await page.getByLabel('Name', { exact: true }).fill('Mine')
|
|
await page.getByLabel('Repositories the agent may use').fill('acme/tools')
|
|
await expect(page.getByLabel('Name', { exact: true })).toHaveValue('Mine')
|
|
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
|
|
})
|
|
|
|
test('each write mode says what it does, and the default is read only', async ({ page }) => {
|
|
await install(page)
|
|
await api(page).install()
|
|
await page.goto(PAGE)
|
|
await page.getByRole('button', { name: 'New GitHub connection' }).click()
|
|
const group = page.getByRole('radiogroup', { name: 'What the agent may change on GitHub' })
|
|
await expect(group.getByRole('radio', { name: /Read only/ })).toBeChecked()
|
|
await expect(group).toContainText('Each change waits for your OK.')
|
|
await expect(group).toContainText('Acts without asking')
|
|
await expect(group).not.toContainText('Allowed')
|
|
})
|
|
|
|
test('the Test result is drawn per repository inside the row, with a tone, and a new run replaces it', async ({ page }) => {
|
|
let n = 0
|
|
await install(page, (repo) => {
|
|
n++
|
|
return repo === 'acme/docs' ? { reachable: false } : { reachable: true, fullName: repo, canWrite: n > 2 }
|
|
})
|
|
await api(page, [BINDING]).install()
|
|
await page.goto(PAGE)
|
|
await page.getByRole('button', { name: 'Test' }).click()
|
|
const row = page.getByTestId('connector-row')
|
|
await expect(row.getByTestId('github-checks')).toContainText('acme/widgets: reachable, read only.')
|
|
await expect(row.getByTestId('github-checks')).toContainText('acme/docs: this token cannot reach it')
|
|
await expect(row.locator('.gh-check--warn')).toHaveCount(1)
|
|
await expect(row.locator('.gh-check--ok')).toHaveCount(1)
|
|
await expect(page.getByText('Connected as support_bot.')).toBeVisible()
|
|
await page.getByRole('button', { name: 'Test' }).click()
|
|
await expect(row.getByTestId('github-checks')).toContainText('acme/widgets: reachable, and the token can write.')
|
|
})
|
|
})
|