Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uitest: Add system for unit testing widgets #534

Merged
merged 2 commits into from
Dec 27, 2024
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
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,5 @@ issues:
- linters: [revive]
text: 'empty-block: this block is empty, you can remove it'

- linters: [musttag]
path: _test.go$
99 changes: 22 additions & 77 deletions internal/forge/github/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import (
"net/http/httptest"
"net/url"
"os/exec"
"reflect"
"strings"
"testing"
"time"

"github.com/charmbracelet/log"
"github.com/rogpeppe/go-internal/testscript"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.abhg.dev/gs/internal/forge"
"go.abhg.dev/gs/internal/secret"
"go.abhg.dev/gs/internal/ui"
"go.abhg.dev/gs/internal/ui/uitest"
Expand Down Expand Up @@ -188,90 +189,34 @@ func TestDeviceFlowAuthenticator(t *testing.T) {
}

func TestSelectAuthenticator(t *testing.T) {
view := uitest.NewEmulatorView(&uitest.EmulatorViewOptions{
Rows: 40,
})

type result struct {
auth authenticator
err error
}
resultc := make(chan result, 1)
go func() {
defer close(resultc)
uitest.RunScripts(t, func(t testing.TB, ts *testscript.TestScript, view ui.InteractiveView) {
wantType := strings.TrimSpace(ts.ReadFile("want_type"))

got, err := selectAuthenticator(view, authenticatorOptions{
auth, err := selectAuthenticator(view, authenticatorOptions{
Endpoint: oauth2.Endpoint{},
})
resultc <- result{got, err}
}()

// TODO: Generalize termtest and use that here
require.EventuallyWithT(t, func(t *assert.CollectT) {
assert.Contains(t,
view.Screen(),
"Select an authentication method",
)
}, time.Second, 50*time.Millisecond)

// Go through all options, roll back around to the first, and select it
for range _authenticationMethods {
require.NoError(t, view.FeedKeys("\x1b[B")) // Down arrow
}
require.NoError(t, view.FeedKeys("\r")) // Enter

select {
case res, ok := <-resultc:
require.True(t, ok)
auth, err := res.auth, res.err
require.NoError(t, err)

_, ok = auth.(*DeviceFlowAuthenticator)
require.True(t, ok, "want *github.DeviceFlowAuthenticator, got %T", auth)

case <-time.After(time.Second):
t.Fatal("timed out")
}
assert.Equal(t, wantType, reflect.TypeOf(auth).String())
}, &uitest.RunScriptsOptions{
Update: *UpdateFixtures,
Rows: 80,
}, "testdata/auth/select")
}

func TestPATAuthenticator(t *testing.T) {
view := uitest.NewEmulatorView(nil)
func TestAuthenticationFlow_PAT(t *testing.T) {
uitest.RunScripts(t, func(t testing.TB, ts *testscript.TestScript, view ui.InteractiveView) {
wantToken := strings.TrimSpace(ts.ReadFile("want_token"))

type result struct {
tok forge.AuthenticationToken
err error
}
resultc := make(chan result, 1)
go func() {
defer close(resultc)

got, err := (&PATAuthenticator{}).Authenticate(context.Background(), view)
resultc <- result{got, err}
}()

// TODO: Generalize termtest and use that here
require.EventuallyWithT(t, func(t *assert.CollectT) {
assert.Contains(t,
view.Screen(),
"Enter Personal Access Token",
)
}, time.Second, 50*time.Millisecond)

require.NoError(t, view.FeedKeys("token\r"))

select {
case res, ok := <-resultc:
require.True(t, ok)
tok, err := res.tok, res.err
got, err := new(Forge).AuthenticationFlow(context.Background(), view)
require.NoError(t, err)

ght, ok := tok.(*AuthenticationToken)
require.True(t, ok, "want *github.AuthenticationToken, got %T", tok)
assert.Equal(t, "token", ght.AccessToken)

case <-time.After(time.Second):
t.Fatal("timed out")
}
assert.Equal(t, &AuthenticationToken{
AccessToken: wantToken,
}, got)
}, &uitest.RunScriptsOptions{
Update: *UpdateFixtures,
Rows: 80,
}, "testdata/auth/pat.txt")
}

func TestAuthCLI(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions internal/forge/github/flag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package github

import "flag"

var UpdateFixtures = flag.Bool("update", false, "update test fixtures")
3 changes: 1 addition & 2 deletions internal/forge/github/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"crypto/rand"
"flag"
"io"
"net/http"
"os"
Expand Down Expand Up @@ -33,7 +32,7 @@ import (
// using recorded fixtures.

var (
_update = flag.Bool("update", false, "update test fixtures")
_update = github.UpdateFixtures
_fixtures = fixturetest.Config{Update: _update}
)

Expand Down
64 changes: 64 additions & 0 deletions internal/forge/github/testdata/auth/pat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
init

await Select an authentication method
feed -r 3 <Down>
snapshot
await
snapshot
cmp stdout select
feed <Enter>

await
snapshot
cmp stdout prompt

feed secret
await
snapshot
cmp stdout filled

feed <Enter>

-- want_token --
secret
-- select --
Select an authentication method:
OAuth
Authorize git-spice to act on your behalf from this device only.
git-spice will get access to all repositories: public and private.
For private repositories, you will need to request installation from a
repository owner.

OAuth: Public repositories only
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to public repositories.

GitHub App
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to repositories where the git-spice GitHub
App is installed explicitly.
Use https://github.com/apps/git-spice to install the App on repositories.
For private repositories, you will need to request installation from a
repository owner.

▶ Personal Access Token
Enter a classic or fine-grained Personal Access Token generated from
https://github.com/settings/tokens.
Classic tokens need at least one of the following scopes: repo or
public_repo.
Fine-grained tokens need read/write access to Repository Contents and Pull
requests.
You can use this method if you do not have the ability to install a GitHub
or OAuth App on your repositories.

GitHub CLI
Re-use an existing GitHub CLI (https://cli.github.com) session.
You must be logged into gh with 'gh auth login' for this to work.
You can use this if you're just experimenting and don't want to set up a
token yet.
-- prompt --
Select an authentication method: Personal Access Token
Enter Personal Access Token:
-- filled --
Select an authentication method: Personal Access Token
Enter Personal Access Token: secret
Empty file.
91 changes: 91 additions & 0 deletions internal/forge/github/testdata/auth/select/oauth.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
init

await Select an authentication method
snapshot
cmp stdout prompt

# go through the list of options and roll back
feed -r 3 <Down>
await
snapshot
cmp stdout down

feed -r 2 <Down>
await
snapshot
cmp stdout prompt

feed <Enter>

-- want_type --
*github.DeviceFlowAuthenticator
-- prompt --
Select an authentication method:
▶ OAuth
Authorize git-spice to act on your behalf from this device only.
git-spice will get access to all repositories: public and private.
For private repositories, you will need to request installation from a
repository owner.

OAuth: Public repositories only
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to public repositories.

GitHub App
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to repositories where the git-spice GitHub
App is installed explicitly.
Use https://github.com/apps/git-spice to install the App on repositories.
For private repositories, you will need to request installation from a
repository owner.

Personal Access Token
Enter a classic or fine-grained Personal Access Token generated from
https://github.com/settings/tokens.
Classic tokens need at least one of the following scopes: repo or
public_repo.
Fine-grained tokens need read/write access to Repository Contents and Pull
requests.
You can use this method if you do not have the ability to install a GitHub
or OAuth App on your repositories.

GitHub CLI
Re-use an existing GitHub CLI (https://cli.github.com) session.
You must be logged into gh with 'gh auth login' for this to work.
You can use this if you're just experimenting and don't want to set up a
token yet.
-- down --
Select an authentication method:
OAuth
Authorize git-spice to act on your behalf from this device only.
git-spice will get access to all repositories: public and private.
For private repositories, you will need to request installation from a
repository owner.

OAuth: Public repositories only
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to public repositories.

GitHub App
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to repositories where the git-spice GitHub
App is installed explicitly.
Use https://github.com/apps/git-spice to install the App on repositories.
For private repositories, you will need to request installation from a
repository owner.

▶ Personal Access Token
Enter a classic or fine-grained Personal Access Token generated from
https://github.com/settings/tokens.
Classic tokens need at least one of the following scopes: repo or
public_repo.
Fine-grained tokens need read/write access to Repository Contents and Pull
requests.
You can use this method if you do not have the ability to install a GitHub
or OAuth App on your repositories.

GitHub CLI
Re-use an existing GitHub CLI (https://cli.github.com) session.
You must be logged into gh with 'gh auth login' for this to work.
You can use this if you're just experimenting and don't want to set up a
token yet.
85 changes: 85 additions & 0 deletions internal/forge/github/testdata/auth/select/oauth_public.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
init

await Select an authentication method
snapshot
cmp stdout prompt

feed <Down>
await
snapshot
cmp stdout select

feed <Enter>

-- want_type --
*github.DeviceFlowAuthenticator
-- prompt --
Select an authentication method:
▶ OAuth
Authorize git-spice to act on your behalf from this device only.
git-spice will get access to all repositories: public and private.
For private repositories, you will need to request installation from a
repository owner.

OAuth: Public repositories only
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to public repositories.

GitHub App
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to repositories where the git-spice GitHub
App is installed explicitly.
Use https://github.com/apps/git-spice to install the App on repositories.
For private repositories, you will need to request installation from a
repository owner.

Personal Access Token
Enter a classic or fine-grained Personal Access Token generated from
https://github.com/settings/tokens.
Classic tokens need at least one of the following scopes: repo or
public_repo.
Fine-grained tokens need read/write access to Repository Contents and Pull
requests.
You can use this method if you do not have the ability to install a GitHub
or OAuth App on your repositories.

GitHub CLI
Re-use an existing GitHub CLI (https://cli.github.com) session.
You must be logged into gh with 'gh auth login' for this to work.
You can use this if you're just experimenting and don't want to set up a
token yet.
-- select --
Select an authentication method:
OAuth
Authorize git-spice to act on your behalf from this device only.
git-spice will get access to all repositories: public and private.
For private repositories, you will need to request installation from a
repository owner.

▶ OAuth: Public repositories only
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to public repositories.

GitHub App
Authorize git-spice to act on your behalf from this device only.
git-spice will only get access to repositories where the git-spice GitHub
App is installed explicitly.
Use https://github.com/apps/git-spice to install the App on repositories.
For private repositories, you will need to request installation from a
repository owner.

Personal Access Token
Enter a classic or fine-grained Personal Access Token generated from
https://github.com/settings/tokens.
Classic tokens need at least one of the following scopes: repo or
public_repo.
Fine-grained tokens need read/write access to Repository Contents and Pull
requests.
You can use this method if you do not have the ability to install a GitHub
or OAuth App on your repositories.

GitHub CLI
Re-use an existing GitHub CLI (https://cli.github.com) session.
You must be logged into gh with 'gh auth login' for this to work.
You can use this if you're just experimenting and don't want to set up a
token yet.
Loading
Loading