-
Notifications
You must be signed in to change notification settings - Fork 8k
Feature detect v1 projects on web mode issue create #10818
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import ( | |
| "testing" | ||
|
|
||
| "github.com/MakeNowJust/heredoc" | ||
| "github.com/cli/cli/v2/internal/gh" | ||
| "github.com/cli/cli/v2/internal/ghrepo" | ||
| "github.com/cli/cli/v2/pkg/httpmock" | ||
| "github.com/stretchr/testify/assert" | ||
|
|
@@ -212,37 +213,16 @@ func Test_RepoMetadata(t *testing.T) { | |
| } | ||
| } | ||
|
|
||
| func Test_ProjectsToPaths(t *testing.T) { | ||
| expectedProjectPaths := []string{"OWNER/REPO/PROJECT_NUMBER", "ORG/PROJECT_NUMBER", "OWNER/REPO/PROJECT_NUMBER_2"} | ||
| projects := []RepoProject{ | ||
| {ID: "id1", Name: "My Project", ResourcePath: "/OWNER/REPO/projects/PROJECT_NUMBER"}, | ||
| {ID: "id2", Name: "Org Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER"}, | ||
| {ID: "id3", Name: "Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER_2"}, | ||
| } | ||
| projectsV2 := []ProjectV2{ | ||
| {ID: "id4", Title: "My Project V2", ResourcePath: "/OWNER/REPO/projects/PROJECT_NUMBER_2"}, | ||
| {ID: "id5", Title: "Org Project V2", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER_3"}, | ||
| } | ||
| projectNames := []string{"My Project", "Org Project", "My Project V2"} | ||
|
|
||
| projectPaths, err := ProjectsToPaths(projects, projectsV2, projectNames) | ||
| if err != nil { | ||
| t.Errorf("error resolving projects: %v", err) | ||
| } | ||
| if !sliceEqual(projectPaths, expectedProjectPaths) { | ||
| t.Errorf("expected projects %v, got %v", expectedProjectPaths, projectPaths) | ||
| } | ||
| } | ||
|
|
||
| func Test_ProjectNamesToPaths(t *testing.T) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you renamed |
||
| http := &httpmock.Registry{} | ||
| client := newTestClient(http) | ||
| t.Run("when projectsV1 is supported, requests them", func(t *testing.T) { | ||
| http := &httpmock.Registry{} | ||
| client := newTestClient(http) | ||
|
|
||
| repo, _ := ghrepo.FromFullName("OWNER/REPO") | ||
| repo, _ := ghrepo.FromFullName("OWNER/REPO") | ||
|
|
||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectList\b`), | ||
| httpmock.StringResponse(` | ||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectList\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "repository": { "projects": { | ||
| "nodes": [ | ||
| { "name": "Cleanup", "id": "CLEANUPID", "resourcePath": "/OWNER/REPO/projects/1" }, | ||
|
|
@@ -251,19 +231,19 @@ func Test_ProjectNamesToPaths(t *testing.T) { | |
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectList\b`), | ||
| httpmock.StringResponse(` | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectList\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "organization": { "projects": { | ||
| "nodes": [ | ||
| { "name": "Triage", "id": "TRIAGEID", "resourcePath": "/orgs/ORG/projects/1" } | ||
| ], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "repository": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "CleanupV2", "id": "CLEANUPV2ID", "resourcePath": "/OWNER/REPO/projects/3" }, | ||
|
|
@@ -272,19 +252,19 @@ func Test_ProjectNamesToPaths(t *testing.T) { | |
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "organization": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "TriageV2", "id": "TRIAGEV2ID", "resourcePath": "/orgs/ORG/projects/2" } | ||
| ], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query UserProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| http.Register( | ||
| httpmock.GraphQL(`query UserProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "viewer": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "MonalisaV2", "id": "MONALISAV2ID", "resourcePath": "/users/MONALISA/projects/5" } | ||
|
|
@@ -293,15 +273,110 @@ func Test_ProjectNamesToPaths(t *testing.T) { | |
| } } } } | ||
| `)) | ||
|
|
||
| projectPaths, err := ProjectNamesToPaths(client, repo, []string{"Triage", "Roadmap", "TriageV2", "RoadmapV2", "MonalisaV2"}) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
| projectPaths, err := ProjectNamesToPaths(client, repo, []string{"Triage", "Roadmap", "TriageV2", "RoadmapV2", "MonalisaV2"}, gh.ProjectsV1Supported) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
|
|
||
| expectedProjectPaths := []string{"ORG/1", "OWNER/REPO/2", "ORG/2", "OWNER/REPO/4", "MONALISA/5"} | ||
| if !sliceEqual(projectPaths, expectedProjectPaths) { | ||
| t.Errorf("expected projects paths %v, got %v", expectedProjectPaths, projectPaths) | ||
| } | ||
| expectedProjectPaths := []string{"ORG/1", "OWNER/REPO/2", "ORG/2", "OWNER/REPO/4", "MONALISA/5"} | ||
| if !sliceEqual(projectPaths, expectedProjectPaths) { | ||
| t.Errorf("expected projects paths %v, got %v", expectedProjectPaths, projectPaths) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("when projectsV1 is not supported, does not request them", func(t *testing.T) { | ||
| http := &httpmock.Registry{} | ||
| client := newTestClient(http) | ||
|
|
||
| repo, _ := ghrepo.FromFullName("OWNER/REPO") | ||
|
|
||
| http.Exclude( | ||
| t, | ||
| httpmock.GraphQL(`query RepositoryProjectList\b`), | ||
| ) | ||
| http.Exclude( | ||
| t, | ||
| httpmock.GraphQL(`query OrganizationProjectList\b`), | ||
| ) | ||
|
|
||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "repository": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "CleanupV2", "id": "CLEANUPV2ID", "resourcePath": "/OWNER/REPO/projects/3" }, | ||
| { "title": "RoadmapV2", "id": "ROADMAPV2ID", "resourcePath": "/OWNER/REPO/projects/4" } | ||
| ], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "organization": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "TriageV2", "id": "TRIAGEV2ID", "resourcePath": "/orgs/ORG/projects/2" } | ||
| ], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query UserProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "viewer": { "projectsV2": { | ||
| "nodes": [ | ||
| { "title": "MonalisaV2", "id": "MONALISAV2ID", "resourcePath": "/users/MONALISA/projects/5" } | ||
| ], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
|
|
||
| projectPaths, err := ProjectNamesToPaths(client, repo, []string{"TriageV2", "RoadmapV2", "MonalisaV2"}, gh.ProjectsV1Unsupported) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
|
|
||
| expectedProjectPaths := []string{"ORG/2", "OWNER/REPO/4", "MONALISA/5"} | ||
| if !sliceEqual(projectPaths, expectedProjectPaths) { | ||
| t.Errorf("expected projects paths %v, got %v", expectedProjectPaths, projectPaths) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("when a project is not found, returns an error", func(t *testing.T) { | ||
| http := &httpmock.Registry{} | ||
| client := newTestClient(http) | ||
|
|
||
| repo, _ := ghrepo.FromFullName("OWNER/REPO") | ||
|
|
||
| // No projects found | ||
| http.Register( | ||
| httpmock.GraphQL(`query RepositoryProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "repository": { "projectsV2": { | ||
| "nodes": [], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query OrganizationProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "organization": { "projectsV2": { | ||
| "nodes": [], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
| http.Register( | ||
| httpmock.GraphQL(`query UserProjectV2List\b`), | ||
| httpmock.StringResponse(` | ||
| { "data": { "viewer": { "projectsV2": { | ||
| "nodes": [], | ||
| "pageInfo": { "hasNextPage": false } | ||
| } } } } | ||
| `)) | ||
|
|
||
| _, err := ProjectNamesToPaths(client, repo, []string{"TriageV2"}, gh.ProjectsV1Unsupported) | ||
| require.Equal(t, err, fmt.Errorf("'TriageV2' not found")) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for a test for this. However, the error message can be clearer if starts with "project...". |
||
| }) | ||
| } | ||
|
|
||
| func Test_RepoResolveMetadataIDs(t *testing.T) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be functionally the same as the previous
ProjectNamesToPathsbut avoids requesting projectv1 if not supported.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's already a
namefield on projects, I'd rename this toProjectTitlesToPathsto match the related field name (ProjectTitles) to avoid confusion. If you renamed this, then do the same for theprojectNamesparameter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense. I think the function existed for projects v1 only originally, which uses the term "name" but you're right that we're using
titlenow, and it'll make for fewer changes when we remove all this code.