~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

7
254 lines · 13.0 KB Raw History
// A private repository used on the pages, over the spaces alpha: alice's
// `vault`, made private from the command line by setup.mjs, and one she
// makes from the pages. Its records live in its space, not in anyone's
// public repo, and its pages read them from this server's index of that
// space, apart from the appview's. So each write here is checked on the
// page at once, from the space with the command line, as not in the
// writer's public repo, on the page read anew, and as not there at all to
// a browser logged in to no one.

import { expect, test } from '@playwright/test'
import { who, url, records, go, anew, knotless, seen, switchTo, comment, editor, choose, logIn, loggedInAs } from './pages.mjs'

test.describe.configure({ mode: 'serial' })

const vault = 'knotless://alice.test/vault'

/// The record a pull's or issue's page, at `href`, shows.
const uriOf = (href) => {
  const [kind, did, rkey] = new URL(href).pathname.split('/').slice(-3)
  return `at://${did}/dev.knotless.${kind}/${rkey}`
}

/// The page as the members see it, and one for a browser logged in to no
/// one, which finds nothing of it.
let page
let stranger

test.beforeAll(async ({ browser }) => {
  page = await browser.newPage()
  stranger = await browser.newPage()
})

test.afterAll(async () => {
  await page.close()
  await stranger.close()
})

test('logging in as its owner', async () => {
  // To anyone, it is not there.
  await go(page, '/alice.test/vault')
  await expect(page.getByText('Not Found')).toBeVisible()
  await logIn(page, 'alice')
  await expect.poll(() => loggedInAs(page)).toBe('alice.test')
  await anew(page, async (expect) => {
    await expect(page.getByText('Plans not yet for anyone else.')).toBeVisible()
    await expect(page.locator('.tag.private').first()).toBeVisible()
  })
})

test('creating a private repository', async () => {
  await go(page, '/new')
  await page.getByLabel('Repository name').fill('diary')
  await page.getByRole('radio', { name: /^Private/ }).click()
  await page.getByRole('button', { name: 'Create repository' }).click()
  await page.waitForURL(url('/alice.test/diary'))
  await expect(page.locator('.tag.private').first()).toBeVisible()
  expect(knotless('alice', 'ls', 'alice.test')).toMatch(/^diary\t.*private$/m)
  expect((await records('alice', 'dev.knotless.repo')).map((r) => r.uri.split('/').pop())).not.toContain('diary')
  expect(await seen(stranger, '/alice.test/diary')).toBe(404)
  await go(page, '/alice.test?tab=repos')
  await anew(page, (expect) => expect(page.getByRole('link', { name: 'diary' }).first()).toBeVisible())
})

test('defining a label', async () => {
  await go(page, '/alice.test/vault/settings/labels')
  await page.getByRole('button', { name: 'New label' }).click()
  const dialog = page.getByRole('dialog', { name: 'New label' })
  await dialog.getByLabel('Name').fill('kind/secret')
  await dialog.getByRole('button', { name: 'Create label' }).click()
  await expect(dialog).toBeHidden()
  await expect(page.locator('li', { hasText: 'kind/secret' })).toBeVisible()
  expect(knotless('alice', 'labels', vault)).toContain('kind/secret')
  await anew(page, (expect) => expect(page.locator('li', { hasText: 'kind/secret' })).toBeVisible())
})

let pulled

test('opening a pull from a branch', async () => {
  await go(page, '/alice.test/vault/pulls/new')
  await page.getByLabel('From').selectOption({ label: 'alice.test/vault (yours)' })
  await page.getByLabel('Branch').selectOption('plans')
  await expect(page.locator('.compared')).toContainText('Write down the plans')
  await expect(page.getByRole('textbox', { name: 'Title' })).toBeHidden()
  await page.locator('.compare-ready').getByRole('button', { name: 'Open pull' }).click()
  await page.getByRole('textbox', { name: 'Title' }).fill('Write down the plans')
  await page.getByRole('button', { name: 'Open pull' }).click()
  await page.waitForURL(/\/alice\.test\/vault\/pull\//)
  pulled = page.url()
  await expect(page.getByRole('heading', { name: 'Write down the plans' })).toBeVisible()
  expect(knotless('alice', 'list', vault, '--pulls')).toContain('Write down the plans')
  expect((await records('alice', 'dev.knotless.pull')).map((r) => r.value.title)).not.toContain('Write down the plans')
  expect(await seen(stranger, new URL(pulled).pathname)).toBe(404)
  await go(page, '/alice.test/vault/pulls')
  await anew(page, (expect) => expect(page.getByRole('link', { name: 'Write down the plans' })).toBeVisible())
})

const readers = (page) => page.locator('section', { hasText: 'Readers' }).locator('li', { hasText: 'bob.test' })

test('adding a reader', async () => {
  await go(page, '/alice.test/vault/settings/access')
  await page.getByLabel('Add a reader').fill('bob.test')
  await page.locator('form', { has: page.getByLabel('Add a reader') }).getByRole('button', { name: 'Add' }).click()
  await expect(readers(page)).toBeVisible()
  expect(knotless('alice', 'members', vault)).toContain(`${who.bob.did}\tread`)
  await anew(page, (expect) => expect(readers(page)).toBeVisible())
})

test('logging in as a reader', async () => {
  await go(page, '/alice.test/vault')
  await logIn(page, 'bob', async () => {
    await page.getByLabel('Your account').click()
    await page.getByRole('button', { name: 'Add account' }).click()
  })
  await expect.poll(() => loggedInAs(page)).toBe('bob.test')
  await anew(page, (expect) => expect(page.getByText('Plans not yet for anyone else.')).toBeVisible())
  // Not the owner: no settings.
  await expect(page.getByRole('link', { name: 'Settings', exact: true })).toHaveCount(0)
})

let asked

test('opening an issue as a reader', async () => {
  await go(page, '/alice.test/vault/issues/new')
  await page.getByRole('textbox', { name: 'Title' }).fill('The plans need dates')
  await page.getByRole('textbox', { name: 'Description' }).fill('Without them, nothing ships.')
  await page.getByRole('button', { name: 'Open issue' }).click()
  await page.waitForURL(/\/alice\.test\/vault\/issue\//)
  asked = page.url()
  await expect(page.getByRole('heading', { name: 'The plans need dates' })).toBeVisible()
  // In the space, where alice reads it, and not in bob's public repo.
  expect(knotless('alice', 'list', vault, '--issues')).toContain('The plans need dates')
  expect((await records('bob', 'dev.knotless.issue')).map((r) => r.value.title)).not.toContain('The plans need dates')
  expect(await seen(stranger, new URL(asked).pathname)).toBe(404)
  await go(page, '/alice.test/vault/issues')
  await anew(page, (expect) => expect(page.getByRole('link', { name: 'The plans need dates' })).toBeVisible())
})

test('commenting, reacting and editing as a reader', async () => {
  await go(page, asked)
  await page.getByPlaceholder('Leave a comment').fill('By the end of the month?')
  await page.getByRole('button', { name: 'Comment', exact: true }).click()
  await expect(comment(page, 'By the end of the month?')).toBeVisible()
  expect(knotless('alice', 'show', vault, uriOf(asked))).toContain('By the end of the month?')
  expect((await records('bob', 'dev.knotless.comment')).map((r) => r.value.body)).not.toContain('By the end of the month?')
  await anew(page, (expect) => expect(comment(page, 'By the end of the month?')).toBeVisible())

  const thread = page.locator('.reactions').first()
  await thread.locator('summary[aria-label="Add reaction"]').click()
  await thread.getByRole('button', { name: 'Eyes' }).click()
  await expect(thread.getByRole('button', { name: 'Eyes: 1' })).toHaveAttribute('aria-pressed', 'true')
  expect(await records('bob', 'dev.knotless.reaction')).toEqual([])
  await anew(page, (expect) => expect(page.locator('.reactions').first().getByRole('button', { name: 'Eyes: 1' })).toBeVisible())

  await choose(page, 'By the end of the month?', 'Edit')
  await editor(page, 'Update comment').fill('By the end of next month?')
  await page.getByRole('button', { name: 'Update comment' }).click()
  await expect(comment(page, 'By the end of next month?')).toContainText(', edited')
  await anew(page, (expect) => expect(comment(page, 'By the end of next month?')).toContainText(', edited'))
})

test('commenting on a pull as a reader', async () => {
  await go(page, pulled)
  await page.getByPlaceholder('Leave a comment').fill('Looks right to me.')
  await page.getByRole('button', { name: 'Comment', exact: true }).click()
  await expect(comment(page, 'Looks right to me.')).toBeVisible()
  await anew(page, (expect) => expect(comment(page, 'Looks right to me.')).toBeVisible())
})

test('hearing of it as the owner', async () => {
  await switchTo(page, 'alice.test')
  await go(page, '/notifications')
  await anew(page, (expect) => expect(page.getByText('The plans need dates').first()).toBeVisible())
})

test('labelling, closing and hiding as the owner', async () => {
  await go(page, asked)
  await page.locator('.label-menu summary').click()
  await page.locator('.label-menu').getByRole('button', { name: 'kind/secret' }).click()
  await expect(page.locator('.label-menu').getByRole('button', { name: 'kind/secret' })).toHaveAttribute('aria-pressed', 'true')
  await anew(page, (expect) => expect(page.locator('.labels').getByText('kind/secret')).toBeVisible())

  await page.getByRole('button', { name: 'Close issue' }).click()
  await expect(page.getByRole('button', { name: 'Reopen issue' })).toBeVisible()
  await anew(page, (expect) => expect(page.getByRole('button', { name: 'Reopen issue' })).toBeVisible())
  expect(knotless('alice', 'list', vault, '--issues')).toMatch(/^issue\tclosed\t.*\tThe plans need dates$/m)
  await page.getByRole('button', { name: 'Reopen issue' }).click()
  await expect(page.getByRole('button', { name: 'Close issue' })).toBeVisible()
  await anew(page, (expect) => expect(page.getByRole('button', { name: 'Close issue' })).toBeVisible())

  await choose(page, 'By the end of next month?', 'Hide this comment')
  await expect(comment(page, 'By the end of next month?')).toHaveCount(0)
  await anew(page, (expect) => expect(comment(page, 'By the end of next month?')).toHaveCount(0))
})

test('merging a private pull', async () => {
  await go(page, pulled)
  await page.locator('.merge-box').getByRole('button', { name: 'Merge pull' }).click()
  const said = page.locator('.event', { hasText: 'Merged as' })
  await expect(said.getByText(/^[0-9a-f]{7}$/)).toBeVisible()
  await anew(page, (expect) => expect(page.locator('.event', { hasText: 'Merged as' })).toBeVisible())
  expect(knotless('alice', 'refs', vault)).toMatch(/refs\/heads\/main/)
  await go(page, '/alice.test/vault/commits/main')
  await anew(page, (expect) => expect(page.getByText('Write down the plans').first()).toBeVisible())
})

test('forking a private repository', async () => {
  await switchTo(page, 'bob.test')
  await go(page, '/alice.test/vault')
  await page.locator('.fork-menu summary').click()
  await page.getByRole('button', { name: 'Create fork' }).click()
  await page.waitForURL(url('/bob.test/vault'))
  await expect(page.getByText('Forked from')).toBeVisible()
  await expect(page.locator('.tag.private').first()).toBeVisible()
  // Private as what it forked: in no public repo, and to no one else.
  expect((await records('bob', 'dev.knotless.repo')).map((r) => r.uri.split('/').pop())).not.toContain('vault')
  expect(await seen(stranger, '/bob.test/vault')).toBe(404)
  await anew(page, (expect) => expect(page.getByText('Plans not yet for anyone else.')).toBeVisible())
})

test('removing a reader', async () => {
  await switchTo(page, 'alice.test')
  await go(page, '/alice.test/vault/settings/access')
  await readers(page).getByRole('button', { name: 'Remove' }).click()
  await expect(readers(page)).toHaveCount(0)
  expect(knotless('alice', 'members', vault)).not.toContain(who.bob.did)
  await anew(page, (expect) => expect(readers(page)).toHaveCount(0))
  // bob reads it no more, once the server asks the space again who may,
  // which it does now and then rather than on every look.
  await switchTo(page, 'bob.test')
  await go(page, '/alice.test/vault')
  await anew(page, (expect) => expect(page.getByText('Not Found')).toBeVisible(), 60_000)
})

test('renaming and deleting a private repository', async () => {
  await switchTo(page, 'alice.test')
  await go(page, '/alice.test/diary/settings')
  await page.getByLabel('Name', { exact: true }).fill('journal')
  await page.getByRole('button', { name: 'Rename' }).click()
  await page.waitForURL(/\/alice\.test\/journal/)
  expect(knotless('alice', 'ls', 'alice.test')).toMatch(/^journal\t.*private$/m)
  await anew(page, async (expect) => {
    await go(page, '/alice.test/diary')
    expect(new URL(page.url()).pathname).toMatch(/^\/alice\.test\/journal/)
  })
  await go(page, '/alice.test/journal/settings')
  await page.getByRole('button', { name: 'Delete', exact: true }).click()
  const dialog = page.locator('#delete-repo')
  await dialog.getByRole('textbox').fill('alice.test/journal')
  await dialog.getByRole('button', { name: 'Delete this repository' }).click()
  await page.waitForURL((u) => !u.pathname.startsWith('/alice.test/journal'))
  expect(knotless('alice', 'ls', 'alice.test')).not.toMatch(/^journal\t/m)
  await go(page, '/alice.test/journal')
  await anew(page, (expect) => expect(page.getByText('Not Found')).toBeVisible())
})