Skip to content

Commit

Permalink
Pull Requests for Data (#8235)
Browse files Browse the repository at this point in the history
* Enable PRs tab

* Initial playwright test

* Fix test

* Improve test

* Cleanup
  • Loading branch information
itaigilo authored Sep 30, 2024
1 parent ff6a238 commit e52707d
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 99 deletions.
15 changes: 4 additions & 11 deletions webui/src/lib/components/repository/tabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import {Link, NavItem} from "../nav";
import {useRouter} from "../../hooks/router";
import {RefTypeBranch} from "../../../constants";

// TODO (gilo): this is temp, until PRfD will be ready
const showPulls = false;

export const RepositoryNavTabs = ({ active }) => {
const { reference } = useRefs();
const router = useRouter();
Expand Down Expand Up @@ -60,14 +57,10 @@ export const RepositoryNavTabs = ({ active }) => {
<Link active={active === 'tags'} href={`/repositories/${repoId}/tags`} component={NavItem}>
<TagIcon/> Tags
</Link>
{
// TODO (gilo): this is temp, until PRfD will be ready
showPulls &&
<Link active={active === 'pulls'} href={`/repositories/${repoId}/pulls`} component={NavItem}>
{/* TODO (gilo): the icon is very similar to the compare icon, consider changing it*/}
<GitPullRequestIcon/> Pull Requests
</Link>
}
<Link active={active === 'pulls'} href={`/repositories/${repoId}/pulls`} component={NavItem}>
{/* TODO (gilo): the icon is very similar to the compare icon, consider changing it*/}
<GitPullRequestIcon/> Pull Requests
</Link>
<Link active={active === 'compare'} href={withRefAndCompareContext(`/repositories/${repoId}/compare`)} component={NavItem}>
<GitCompareIcon/> Compare
</Link>
Expand Down
6 changes: 3 additions & 3 deletions webui/src/pages/repositories/repository/pulls/pullsList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ const PullWidget = ({repo, pull}) => {
<h6>
<PullIcon status={pull.status}/>
{" "}
<Link href={{
<Link className="pull-title" href={{
pathname: '/repositories/:repoId/pulls/:pullId',
params: {repoId: repo.id, pullId: pull.id}
}}>
{pull.title}
</Link>
</h6>
<small>{getDescription()}</small>
<small className="pull-description">{getDescription()}</small>
</div>
<div className="float-end mt-2">
<div className="btn btn-light btn-sm">{pull.destination_branch}</div>
Expand All @@ -81,7 +81,7 @@ const PullsList = ({repo, after, prefix, onPaginate}) => {
else content = (results && !!results.length ?
<>
<Card>
<ListGroup variant="flush">
<ListGroup variant="flush" className="pulls-list">
{results.map(pull => (
<PullWidget key={pull.id} repo={repo} pull={pull}/>
))}
Expand Down
215 changes: 130 additions & 85 deletions webui/test/e2e/common/quickstart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,146 @@ import { test, expect } from "@playwright/test";
import { RepositoriesPage } from "../poms/repositoriesPage";
import { RepositoryPage } from "../poms/repositoryPage";
import { ObjectViewerPage } from "../poms/objectViewerPage";
import { PullsPage } from "../poms/pullsPage";

const QUICKSTART_REPO_NAME = "quickstart";
const PARQUET_OBJECT_NAME = "lakes.parquet";
const NEW_BRANCH_NAME = "denmark-lakes";

const SELECT_QUERY =
"SELECT country, COUNT(*) FROM READ_PARQUET('lakefs://quickstart/main/lakes.parquet') GROUP BY country ORDER BY COUNT(*) DESC LIMIT 5;";
"SELECT country, COUNT(*) FROM READ_PARQUET('lakefs://quickstart/main/lakes.parquet') GROUP BY country ORDER BY COUNT(*) DESC LIMIT 5;";
const CREATE_TABLE_QUERY =
"CREATE OR REPLACE TABLE lakes AS SELECT * FROM READ_PARQUET('lakefs://quickstart/denmark-lakes/lakes.parquet');";
"CREATE OR REPLACE TABLE lakes AS SELECT * FROM READ_PARQUET('lakefs://quickstart/denmark-lakes/lakes.parquet');";
const DELETE_QUERY = "DELETE FROM lakes WHERE Country != 'Denmark';";
const COPY_QUERY =
"COPY lakes TO 'lakefs://quickstart/denmark-lakes/lakes.parquet';";
"COPY lakes TO 'lakefs://quickstart/denmark-lakes/lakes.parquet';";
const SELECT_NEW_BRANCH =
"DROP TABLE lakes; SELECT country, COUNT(*) FROM READ_PARQUET('lakefs://quickstart/denmark-lakes/lakes.parquet') GROUP BY country ORDER BY COUNT(*) DESC LIMIT 5;";
"DROP TABLE lakes; SELECT country, COUNT(*) FROM READ_PARQUET('lakefs://quickstart/denmark-lakes/lakes.parquet') GROUP BY country ORDER BY COUNT(*) DESC LIMIT 5;";

test.describe("Quickstart", () => {
test.describe.configure({ mode: "serial" });
test("create repo w/ sample data", async ({ page }) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.createRepository(QUICKSTART_REPO_NAME, true);
});

test("view and query parquet object", async ({ page }) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();

const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(SELECT_QUERY);
await objectViewerPage.clickExecuteButton();
await expect(await objectViewerPage.getResultRowCount()).toEqual(5);
});

test("transforming data", async ({ page }) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.createBranch(NEW_BRANCH_NAME);

await repositoryPage.gotoObjectsTab();
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();

const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(CREATE_TABLE_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(DELETE_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(COPY_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(SELECT_NEW_BRANCH);
await objectViewerPage.clickExecuteButton();
await expect(await objectViewerPage.getResultRowCount()).toEqual(1);
});

test("commit and merge", async ({ page }) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.gotoUncommittedChangeTab();
await repositoryPage.switchBranch(NEW_BRANCH_NAME);
await expect(
await page.getByText("Showing changes for branch")
).toBeVisible();
await expect(await repositoryPage.getUncommittedCount()).toEqual(1);

await repositoryPage.commitChanges("denmark");
await expect(page.getByText("No changes")).toBeVisible();

await repositoryPage.gotoCompareTab();
await repositoryPage.switchBaseBranch("main");
await expect(await page.getByText("Showing changes between")).toBeVisible();
await expect(await repositoryPage.getUncommittedCount()).toEqual(1);
await repositoryPage.merge("merge commit");
await expect(page.getByText("No changes")).toBeVisible();

await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();
const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(SELECT_QUERY);
await objectViewerPage.clickExecuteButton();
await expect(await objectViewerPage.getResultRowCount()).toEqual(1);
});
test.describe.configure({mode: "serial"});
test("create repo w/ sample data", async ({page}) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.createRepository(QUICKSTART_REPO_NAME, true);
});

test("view and query parquet object", async ({page}) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();

const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(SELECT_QUERY);
await objectViewerPage.clickExecuteButton();
expect(await objectViewerPage.getResultRowCount()).toEqual(5);
});

test("transforming data", async ({page}) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.createBranch(NEW_BRANCH_NAME);

await repositoryPage.gotoObjectsTab();
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();

const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(CREATE_TABLE_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(DELETE_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(COPY_QUERY);
await objectViewerPage.clickExecuteButton();

await objectViewerPage.enterQuery(SELECT_NEW_BRANCH);
await objectViewerPage.clickExecuteButton();
expect(await objectViewerPage.getResultRowCount()).toEqual(1);
});

test("commit and merge", async ({page}) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const repositoryPage = new RepositoryPage(page);
await repositoryPage.gotoUncommittedChangeTab();
await repositoryPage.switchBranch(NEW_BRANCH_NAME);
await expect(page.getByText("Showing changes for branch")).toBeVisible();
expect(await repositoryPage.getUncommittedCount()).toEqual(1);

await repositoryPage.commitChanges("denmark");
await expect(page.getByText("No changes")).toBeVisible();

await repositoryPage.gotoCompareTab();
await repositoryPage.switchBaseBranch("main");
await expect(page.getByText("Showing changes between")).toBeVisible();
expect(await repositoryPage.getUncommittedCount()).toEqual(1);
await repositoryPage.merge("merge commit");
await expect(page.getByText("No changes")).toBeVisible();

await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);
await repositoryPage.clickObject(PARQUET_OBJECT_NAME);
await expect(page.getByText("Loading...")).not.toBeVisible();
const objectViewerPage = new ObjectViewerPage(page);
await objectViewerPage.enterQuery(SELECT_QUERY);
await objectViewerPage.clickExecuteButton();
expect(await objectViewerPage.getResultRowCount()).toEqual(1);
});

test("pull requests", async ({page}) => {
const repositoriesPage = new RepositoriesPage(page);
await repositoriesPage.goto();
await repositoriesPage.goToRepository(QUICKSTART_REPO_NAME);

const branchNameForPull = "branch-for-pull-1";

const repositoryPage = new RepositoryPage(page);
await repositoryPage.createBranch(branchNameForPull);

// delete a file in the branch
await repositoryPage.gotoObjectsTab();
await repositoryPage.switchBranch(branchNameForPull);
await repositoryPage.deleteFirstObjectInDirectory("images/");

// commit the change
await repositoryPage.gotoUncommittedChangeTab();
expect(await repositoryPage.getUncommittedCount()).toEqual(1);
await repositoryPage.commitChanges("Commit for pull-1");
await expect(page.getByText("No changes")).toBeVisible();

// pulls list sanity
await repositoryPage.gotoPullRequestsTab();
await expect(page.getByText("Create Pull Request")).toBeVisible();
const pullsPage = new PullsPage(page);
expect(await pullsPage.getPullsListCount()).toEqual(0);

// create a pull request
await pullsPage.clickCreatePullButton();
await expect(page.getByRole("heading", {name: "Create Pull Request"})).toBeVisible();
await pullsPage.switchCompareBranch(branchNameForPull);
const pullDetails = {title: "PR for branch 1", description: "A description for PR 1"};
await pullsPage.fillPullTitle(pullDetails.title);
await pullsPage.fillPullDescription(pullDetails.description);
await pullsPage.clickCreatePullButton();
expect(await pullsPage.getBranchesCompareURI()).toEqual(`main...${branchNameForPull}/`);

// merge the pull request
await pullsPage.clickMergePullButton();
await repositoryPage.gotoPullRequestsTab();
await pullsPage.gotoPullsTab("closed");
const firstPullRowDetails = await pullsPage.getFirstPullsRowDetails();
expect(firstPullRowDetails.title).toEqual(pullDetails.title);
expect(firstPullRowDetails.description).toMatch(/^Merged/)
});
});
53 changes: 53 additions & 0 deletions webui/test/e2e/poms/pullsPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Locator, Page } from "@playwright/test";

export class PullsPage {
private page: Page;

constructor(page: Page) {
this.page = page;
}

async getPullsListCount(): Promise<number> {
await this.page.locator("div.pulls-list").isVisible();
return this.page
.locator("div.pulls-list")
.locator("pull-row")
.count();
}

async switchCompareBranch(name: string): Promise<void> {
await this.page.getByRole("button", {name: "to branch: "}).click();
await this.page.getByRole("button", {name}).click();
}

async clickCreatePullButton(): Promise<void> {
await this.page.getByRole("button", {name: "Create Pull Request"}).click();
}

async getBranchesCompareURI(): Promise<string> {
return await this.page.locator("div.lakefs-uri").innerText();
}

async clickMergePullButton(): Promise<void> {
await this.page.getByRole("button", {name: "Merge pull request"}).click();
}

async fillPullTitle(title: string): Promise<void> {
await this.page.getByPlaceholder("Add a title...").fill(title);
}

async fillPullDescription(description: string): Promise<void> {
await this.page.getByPlaceholder("Describe your changes...").fill(description);
}

async gotoPullsTab(id: string): Promise<void> {
await this.page.locator(`#pulls-tabs-tab-${id}`).click();
}

async getFirstPullsRowDetails(): Promise<{title: string, description: string}> {
const firstPullRow = this.page.locator("div.pull-row").first();
const title = await firstPullRow.locator(".pull-title").innerText();
const description = await firstPullRow.locator(".pull-description").innerText();
return {title, description};
}
}
Loading

0 comments on commit e52707d

Please sign in to comment.