Add integration test suite for campaigns (#13142)

This commit is contained in:
Erik Seliger 2020-08-20 17:01:46 +02:00 committed by GitHub
parent 09ddebfeae
commit 61d1098ce7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 705 additions and 40 deletions

1
.github/CODEOWNERS vendored
View File

@ -146,6 +146,7 @@
/enterprise/cmd/frontend/internal/campaigns @sourcegraph/campaigns
/internal/campaigns @sourcegraph/campaigns
/web/**/campaigns/** @sourcegraph/campaigns
/web/src/integration/campaigns.test.ts @sourcegraph/campaigns
# Search
*/search/**/* @sourcegraph/search

View File

@ -108,6 +108,7 @@ func addSharedTests(c Config) func(pipeline *bk.Pipeline) {
// Client integration tests
pipeline.AddStep(":puppeteer::electric_plug:",
bk.Env("PUPPETEER_SKIP_CHROMIUM_DOWNLOAD", ""),
bk.Env("ENTERPRISE", "1"),
bk.Cmd("COVERAGE_INSTRUMENT=true dev/ci/yarn-run.sh build-web"),
bk.Cmd("yarn run cover-integration"),
bk.Cmd("yarn nyc report -r json"),

View File

@ -89,7 +89,7 @@ export class FileDiffNode extends React.PureComponent<FileDiffNodeProps, State>
return (
<>
<a id={anchor} />
<div className={`file-diff-node card ${this.props.className || ''}`}>
<div className={`file-diff-node test-file-diff-node card ${this.props.className || ''}`}>
<div className="card-header file-diff-node__header">
<button type="button" className="btn btn-sm btn-icon mr-2" onClick={this.toggleExpand}>
{this.state.expanded ? (

View File

@ -61,7 +61,7 @@ export const CampaignApplyPage: React.FunctionComponent<CampaignApplyPageProps>
createdAt={spec.createdAt}
creator={spec.creator}
verb="Uploaded"
className="mb-3"
className="mb-3 test-campaign-apply-page"
/>
<CreateUpdateCampaignAlert
history={history}

View File

@ -60,7 +60,7 @@ export const CreateUpdateCampaignAlert: React.FunctionComponent<CreateUpdateCamp
<button
type="button"
className={classNames(
'btn btn-primary',
'btn btn-primary test-campaigns-confirm-apply-btn',
isLoading === true || (!viewerCanAdminister && 'disabled')
)}
onClick={onApply}

View File

@ -61,7 +61,7 @@ export const VisibleChangesetSpecNode: React.FunctionComponent<VisibleChangesetS
<>
<button
type="button"
className="btn btn-icon"
className="btn btn-icon test-campaigns-expand-changeset-spec"
aria-label={isExpanded ? 'Collapse section' : 'Expand section'}
onClick={toggleIsExpanded}
>

View File

@ -61,6 +61,7 @@ export const CampaignCloseAlert: React.FunctionComponent<CampaignCloseAlertProps
type="checkbox"
checked={closeChangesets}
onChange={onChangeCloseChangesets}
className="test-campaigns-close-changesets-checkbox"
disabled={isClosing === true || !viewerCanAdminister}
/>{' '}
Also close open changesets on code hosts.
@ -77,7 +78,7 @@ export const CampaignCloseAlert: React.FunctionComponent<CampaignCloseAlertProps
<div className="d-flex justify-content-end">
<button
type="button"
className="btn btn-secondary mr-3"
className="btn btn-secondary mr-3 test-campaigns-close-abort-btn"
onClick={onCancel}
disabled={isClosing === true || !viewerCanAdminister}
>
@ -85,7 +86,7 @@ export const CampaignCloseAlert: React.FunctionComponent<CampaignCloseAlertProps
</button>
<button
type="button"
className="btn btn-danger"
className="btn btn-danger test-campaigns-confirm-close-btn"
onClick={onClose}
disabled={isClosing === true || !viewerCanAdminister}
>

View File

@ -79,7 +79,7 @@ export const CampaignClosePage: React.FunctionComponent<CampaignClosePageProps>
namespace={campaign.namespace}
creator={campaign.initialApplier}
createdAt={campaign.createdAt}
className="mb-3"
className="mb-3 test-campaign-close-page"
/>
<CampaignCloseAlert
campaignID={campaignID}
@ -90,7 +90,11 @@ export const CampaignClosePage: React.FunctionComponent<CampaignClosePageProps>
closeCampaign={closeCampaign}
viewerCanAdminister={campaign.viewerCanAdminister}
/>
{closeChangesets && <h2>Closing the campaign will close the following changesets:</h2>}
{closeChangesets && (
<h2 className="test-campaigns-close-willclose-header">
Closing the campaign will close the following changesets:
</h2>
)}
{!closeChangesets && <h2>The following changesets will remain open:</h2>}
<CampaignCloseChangesetsList
campaignID={campaignID}

View File

@ -57,7 +57,7 @@ export const ExternalChangesetCloseNode: React.FunctionComponent<ExternalChanges
<>
<button
type="button"
className="btn btn-icon"
className="btn btn-icon test-campaigns-expand-changeset"
aria-label={isExpanded ? 'Collapse section' : 'Expand section'}
onClick={toggleIsExpanded}
>

View File

@ -109,7 +109,7 @@ export const CampaignBurndownChart: React.FunctionComponent<Props> = ({
)
}
return (
<ResponsiveContainer width={width} height={300}>
<ResponsiveContainer width={width} height={300} className="test-campaigns-chart">
<ComposedChart
data={changesetCountsOverTime.map(snapshot => ({ ...snapshot, date: Date.parse(snapshot.date) }))}
>

View File

@ -115,7 +115,7 @@ export const CampaignDetails: React.FunctionComponent<CampaignDetailsProps> = ({
history={history}
/>
}
className="mb-3"
className="mb-3 test-campaign-details-page"
/>
<CampaignStatsCard closedAt={campaign.closedAt} stats={campaign.changesets.stats} className="mb-3" />
<CampaignDescription history={history} description={campaign.description} />

View File

@ -41,7 +41,7 @@ export const CampaignDetailsActionSection: React.FunctionComponent<CampaignDetai
return (
<button
type="button"
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-delete-btn"
onClick={onDeleteCampaign}
data-tooltip="Deleting this campaign is a final action."
disabled={isDeleting === true}
@ -54,7 +54,7 @@ export const CampaignDetailsActionSection: React.FunctionComponent<CampaignDetai
return (
<Link
to={`${location.pathname}/close`}
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-close-btn"
data-tooltip="View a preview of all changes that will happen when you close this campaign."
>
<DeleteIcon className="icon-inline" /> Close

View File

@ -72,7 +72,7 @@ export const CampaignTabs: React.FunctionComponent<CampaignTabsProps> = ({
<SourceBranchIcon className="icon-inline text-muted mr-1" /> Changesets
</a>
</li>
<li className="nav-item">
<li className="nav-item test-campaigns-chart-tab">
<a
href=""
onClick={onSelectChart}

View File

@ -38,12 +38,13 @@ exports[`CampaignBurndownChart renders the chart 1`] = `
width={500}
>
<ResponsiveContainer
className="test-campaigns-chart"
debounce={0}
height={300}
width={500}
>
<div
className="recharts-responsive-container"
className="recharts-responsive-container test-campaigns-chart"
style={
Object {
"height": 300,

View File

@ -29,7 +29,7 @@ exports[`CampaignDetails viewerCanAdminister: false viewing existing 1`] = `
history="[History]"
/>
}
className="mb-3"
className="mb-3 test-campaign-details-page"
createdAt="2020-01-01"
creator={
Object {
@ -46,7 +46,7 @@ exports[`CampaignDetails viewerCanAdminister: false viewing existing 1`] = `
}
>
<div
className="d-flex w-100 mb-2 justify-content-between align-items-center mb-3"
className="d-flex w-100 mb-2 justify-content-between align-items-center mb-3 test-campaign-details-page"
>
<div>
<h1
@ -123,12 +123,12 @@ exports[`CampaignDetails viewerCanAdminister: false viewing existing 1`] = `
history="[History]"
>
<AnchorLink
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-close-btn"
data-tooltip="View a preview of all changes that will happen when you close this campaign."
to="//close"
>
<a
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-close-btn"
data-tooltip="View a preview of all changes that will happen when you close this campaign."
href="//close"
>
@ -497,7 +497,7 @@ exports[`CampaignDetails viewerCanAdminister: false viewing existing 1`] = `
</a>
</li>
<li
className="nav-item"
className="nav-item test-campaigns-chart-tab"
>
<a
className="nav-link"
@ -810,7 +810,7 @@ exports[`CampaignDetails viewerCanAdminister: true viewing existing 1`] = `
history="[History]"
/>
}
className="mb-3"
className="mb-3 test-campaign-details-page"
createdAt="2020-01-01"
creator={
Object {
@ -827,7 +827,7 @@ exports[`CampaignDetails viewerCanAdminister: true viewing existing 1`] = `
}
>
<div
className="d-flex w-100 mb-2 justify-content-between align-items-center mb-3"
className="d-flex w-100 mb-2 justify-content-between align-items-center mb-3 test-campaign-details-page"
>
<div>
<h1
@ -904,12 +904,12 @@ exports[`CampaignDetails viewerCanAdminister: true viewing existing 1`] = `
history="[History]"
>
<AnchorLink
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-close-btn"
data-tooltip="View a preview of all changes that will happen when you close this campaign."
to="//close"
>
<a
className="btn btn-outline-danger"
className="btn btn-outline-danger test-campaigns-close-btn"
data-tooltip="View a preview of all changes that will happen when you close this campaign."
href="//close"
>
@ -1278,7 +1278,7 @@ exports[`CampaignDetails viewerCanAdminister: true viewing existing 1`] = `
</a>
</li>
<li
className="nav-item"
className="nav-item test-campaigns-chart-tab"
>
<a
className="nav-link"

View File

@ -55,7 +55,7 @@ export const ExternalChangesetNode: React.FunctionComponent<ExternalChangesetNod
<>
<button
type="button"
className="btn btn-icon"
className="btn btn-icon test-campaigns-expand-changeset"
aria-label={isExpanded ? 'Collapse section' : 'Expand section'}
onClick={toggleIsExpanded}
>

View File

@ -4,7 +4,7 @@ exports[`ExternalChangesetNode renders an externalchangeset 1`] = `
<Fragment>
<button
aria-label="Expand section"
className="btn btn-icon"
className="btn btn-icon test-campaigns-expand-changeset"
onClick={[Function]}
type="button"
>

View File

@ -55,7 +55,7 @@ export const CampaignListPage: React.FunctionComponent<Props> = ({
return (
<>
<CampaignHeader
className="mb-3"
className="mb-3 test-campaign-list-page"
actionSection={
<Link to={`${location.pathname}/create`} className="btn btn-primary">
<PlusIcon className="icon-inline" /> New campaign

View File

@ -48,13 +48,18 @@ export const CampaignNode: React.FunctionComponent<CampaignNodeProps> = ({
<h3 className="m-0 d-inline-block">
{displayNamespace && (
<>
<Link className="text-muted" to={`${node.namespace.url}/campaigns`}>
<Link
className="text-muted test-campaign-namespace-link"
to={`${node.namespace.url}/campaigns`}
>
{node.namespace.namespaceName}
</Link>
<span className="text-muted d-inline-block mx-1">/</span>
</>
)}
<Link to={node.url}>{node.name}</Link>
<Link className="test-campaign-link" to={node.url}>
{node.name}
</Link>
</h3>
<small className="ml-2 text-muted">
created{' '}

View File

@ -14,7 +14,7 @@ exports[`CampaignListPage renders for non-siteadmin and totalCount: 0 1`] = `
New campaign
</AnchorLink>
}
className="mb-3"
className="mb-3 test-campaign-list-page"
/>
<CampaignsListBetaNotice />
<FilteredConnection
@ -87,7 +87,7 @@ exports[`CampaignListPage renders for non-siteadmin and totalCount: 1 1`] = `
New campaign
</AnchorLink>
}
className="mb-3"
className="mb-3 test-campaign-list-page"
/>
<CampaignsListBetaNotice />
<FilteredConnection
@ -160,7 +160,7 @@ exports[`CampaignListPage renders for siteadmin and totalCount: 0 1`] = `
New campaign
</AnchorLink>
}
className="mb-3"
className="mb-3 test-campaign-list-page"
/>
<CampaignsListBetaNotice />
<FilteredConnection
@ -233,7 +233,7 @@ exports[`CampaignListPage renders for siteadmin and totalCount: 1 1`] = `
New campaign
</AnchorLink>
}
className="mb-3"
className="mb-3 test-campaign-list-page"
/>
<CampaignsListBetaNotice />
<FilteredConnection

View File

@ -49,11 +49,11 @@ exports[`CampaignNode campaign without description 1`] = `
className="m-0 d-inline-block"
>
<AnchorLink
className="text-muted"
className="text-muted test-campaign-namespace-link"
to="/users/alice/campaigns"
>
<a
className="text-muted"
className="text-muted test-campaign-namespace-link"
href="/users/alice/campaigns"
>
alice
@ -65,9 +65,11 @@ exports[`CampaignNode campaign without description 1`] = `
/
</span>
<AnchorLink
className="test-campaign-link"
to="/users/alice/campaigns/123"
>
<a
className="test-campaign-link"
href="/users/alice/campaigns/123"
>
Upgrade lodash to v4
@ -265,11 +267,11 @@ exports[`CampaignNode closed campaign 1`] = `
className="m-0 d-inline-block"
>
<AnchorLink
className="text-muted"
className="text-muted test-campaign-namespace-link"
to="/users/alice/campaigns"
>
<a
className="text-muted"
className="text-muted test-campaign-namespace-link"
href="/users/alice/campaigns"
>
alice
@ -281,9 +283,11 @@ exports[`CampaignNode closed campaign 1`] = `
/
</span>
<AnchorLink
className="test-campaign-link"
to="/users/alice/campaigns/123"
>
<a
className="test-campaign-link"
href="/users/alice/campaigns/123"
>
Upgrade lodash to v4
@ -489,11 +493,11 @@ exports[`CampaignNode open campaign 1`] = `
className="m-0 d-inline-block"
>
<AnchorLink
className="text-muted"
className="text-muted test-campaign-namespace-link"
to="/users/alice/campaigns"
>
<a
className="text-muted"
className="text-muted test-campaign-namespace-link"
href="/users/alice/campaigns"
>
alice
@ -505,9 +509,11 @@ exports[`CampaignNode open campaign 1`] = `
/
</span>
<AnchorLink
className="test-campaign-link"
to="/users/alice/campaigns/123"
>
<a
className="test-campaign-link"
href="/users/alice/campaigns/123"
>
Upgrade lodash to v4
@ -713,9 +719,11 @@ exports[`CampaignNode open campaign on user page 1`] = `
className="m-0 d-inline-block"
>
<AnchorLink
className="test-campaign-link"
to="/users/alice/campaigns/123"
>
<a
className="test-campaign-link"
href="/users/alice/campaigns/123"
>
Upgrade lodash to v4

View File

@ -0,0 +1,644 @@
import assert from 'assert'
import { createDriverForTest, Driver } from '../../../shared/src/testing/driver'
import { commonWebGraphQlResults } from './graphQlResults'
import { createWebIntegrationTestContext, WebIntegrationTestContext } from './context'
import { saveScreenshotsUponFailures } from '../../../shared/src/testing/screenshotReporter'
import { subDays, addDays } from 'date-fns'
import { createJsContext } from './jscontext'
import {
ChangesetCheckState,
ChangesetExternalState,
ChangesetPublicationState,
ChangesetReconcilerState,
ChangesetReviewState,
ChangesetCountsOverTimeVariables,
ChangesetCountsOverTimeResult,
ExternalChangesetFileDiffsVariables,
ExternalChangesetFileDiffsResult,
CampaignChangesetsVariables,
CampaignChangesetsResult,
WebGraphQlOperations,
CampaignByIDResult,
ExternalChangesetFileDiffsFields,
DiffHunkLineType,
ChangesetSpecType,
ListCampaign,
} from '../graphql-operations'
import { SharedGraphQlOperations } from '../../../shared/src/graphql-operations'
const campaignListNode: ListCampaign = {
id: 'campaign123',
url: '/users/alice/campaigns/campaign123',
name: 'campaign123',
createdAt: subDays(new Date(), 5).toISOString(),
changesets: { stats: { closed: 4, merged: 10, open: 5 } },
closedAt: null,
description: null,
namespace: {
namespaceName: 'alice',
url: '/users/alice',
},
}
const mockDiff: NonNullable<ExternalChangesetFileDiffsFields['diff']> = {
__typename: 'RepositoryComparison',
fileDiffs: {
nodes: [
{
__typename: 'FileDiff',
internalID: 'intid123',
oldPath: '/somefile.md',
newPath: '/somefile.md',
oldFile: {
__typename: 'GitBlob',
binary: false,
byteSize: 0,
},
newFile: {
__typename: 'GitBlob',
binary: false,
byteSize: 0,
},
mostRelevantFile: {
__typename: 'GitBlob',
url: 'http://test.test/fileurl',
},
hunks: [
{
section: "@@ -70,33 +81,154 @@ super('awesome', () => {",
oldRange: {
startLine: 70,
lines: 33,
},
newRange: {
startLine: 81,
lines: 154,
},
oldNoNewlineAt: false,
highlight: {
aborted: false,
lines: [
{
html: 'some fiel content',
kind: DiffHunkLineType.DELETED,
},
{
html: 'some file content',
kind: DiffHunkLineType.ADDED,
},
],
},
},
],
stat: {
added: 10,
changed: 3,
deleted: 8,
},
},
],
pageInfo: {
endCursor: null,
hasNextPage: false,
},
totalCount: 1,
},
range: {
base: {
__typename: 'GitRef',
target: {
oid: 'abc123base',
},
},
head: {
__typename: 'GitRef',
target: {
oid: 'abc123head',
},
},
},
}
const ChangesetCountsOverTime: (variables: ChangesetCountsOverTimeVariables) => ChangesetCountsOverTimeResult = () => ({
node: {
__typename: 'Campaign',
changesetCountsOverTime: [
{
closed: 12,
date: subDays(new Date(), 2).toISOString(),
merged: 10,
openApproved: 3,
openChangesRequested: 1,
openPending: 91,
total: 130,
},
{
closed: 12,
date: subDays(new Date(), 1).toISOString(),
merged: 10,
openApproved: 23,
openChangesRequested: 1,
openPending: 71,
total: 130,
},
],
},
})
const ExternalChangesetFileDiffs: (
variables: ExternalChangesetFileDiffsVariables
) => ExternalChangesetFileDiffsResult = () => ({
node: {
__typename: 'ExternalChangeset',
diff: mockDiff,
},
})
const CampaignChangesets: (variables: CampaignChangesetsVariables) => CampaignChangesetsResult = () => ({
node: {
__typename: 'Campaign',
changesets: {
totalCount: 1,
pageInfo: {
endCursor: null,
hasNextPage: false,
},
nodes: [
{
__typename: 'ExternalChangeset',
body: 'body123',
checkState: ChangesetCheckState.PASSED,
createdAt: subDays(new Date(), 5).toISOString(),
updatedAt: subDays(new Date(), 5).toISOString(),
diffStat: {
added: 100,
changed: 10,
deleted: 23,
},
error: null,
externalID: '123',
externalState: ChangesetExternalState.OPEN,
externalURL: {
url: 'http://test.test/123',
},
id: 'changeset123',
labels: [
{
color: '93ba13',
description: null,
text: 'Abc label',
},
],
nextSyncAt: null,
publicationState: ChangesetPublicationState.PUBLISHED,
reconcilerState: ChangesetReconcilerState.COMPLETED,
repository: {
id: 'repo123',
name: 'github.com/sourcegraph/repo',
url: 'http://test.test/repo',
},
reviewState: ChangesetReviewState.APPROVED,
title: 'The changeset title',
},
],
},
},
})
function mockCommonGraphQLResponses(
entityType: 'user' | 'org',
campaignOverrides?: Partial<NonNullable<CampaignByIDResult['node']>>
): Partial<WebGraphQlOperations & SharedGraphQlOperations> {
const namespaceURL = entityType === 'user' ? '/users/alice' : '/organizations/test-org'
return {
Organization: () => ({
organization: {
__typename: 'Org',
createdAt: '2020-08-07T00:00',
displayName: 'test-org',
settingsURL: `${namespaceURL}/settings`,
id: 'TestOrg',
name: 'test-org',
url: namespaceURL,
viewerCanAdminister: true,
viewerIsMember: false,
viewerPendingInvitation: null,
},
}),
User: () => ({
user: {
__typename: 'User',
id: 'user123',
username: 'alice',
displayName: 'alice',
url: namespaceURL,
settingsURL: `${namespaceURL}/settings`,
avatarURL: '',
viewerCanAdminister: true,
siteAdmin: true,
builtinAuth: true,
createdAt: '2020-04-10T21:11:42Z',
emails: [{ email: 'alice@example.com', verified: true }],
organizations: { nodes: [] },
permissionsInfo: null,
},
}),
CampaignByID: () => ({
node: {
__typename: 'Campaign',
id: 'campaign123',
changesets: { stats: { closed: 2, merged: 3, open: 10, total: 5, unpublished: 3 } },
closedAt: null,
createdAt: subDays(new Date(), 5).toISOString(),
updatedAt: subDays(new Date(), 5).toISOString(),
description: '### Very cool campaign',
initialApplier: {
url: '/users/alice',
username: 'alice',
},
name: 'test-campaign',
namespace: {
namespaceName: entityType === 'user' ? 'alice' : 'test-org',
url: namespaceURL,
},
url: `${namespaceURL}/campaigns/campaign123`,
diffStat: {
added: 1000,
changed: 29,
deleted: 817,
},
viewerCanAdminister: true,
...campaignOverrides,
},
}),
}
}
describe('Campaigns', () => {
let driver: Driver
before(async () => {
driver = await createDriverForTest()
})
after(() => driver?.close())
let testContext: WebIntegrationTestContext
beforeEach(async function () {
testContext = await createWebIntegrationTestContext({
driver,
currentTest: this.currentTest!,
directory: __dirname,
})
testContext.overrideJsContext({
...createJsContext({
sourcegraphBaseUrl: testContext.driver.sourcegraphBaseUrl,
}),
experimentalFeatures: { automation: 'enabled' },
})
})
saveScreenshotsUponFailures(() => driver.page)
afterEach(() => testContext?.dispose())
describe('Campaigns list', () => {
it('lists global campaigns', async () => {
testContext.overrideGraphQL({
...commonWebGraphQlResults,
Campaigns: () => ({
campaigns: {
nodes: [campaignListNode],
pageInfo: {
endCursor: null,
hasNextPage: false,
},
totalCount: 1,
},
}),
})
await driver.page.goto(driver.sourcegraphBaseUrl + '/campaigns')
await driver.page.waitForSelector('.test-campaign-list-page')
await driver.page.waitForSelector('.test-campaign-namespace-link')
await driver.page.waitForSelector('.test-campaign-link')
assert.strictEqual(
await driver.page.evaluate(
() => document.querySelector<HTMLAnchorElement>('.test-campaign-namespace-link')?.href
),
testContext.driver.sourcegraphBaseUrl + '/users/alice/campaigns'
)
assert.strictEqual(
await driver.page.evaluate(
() => document.querySelector<HTMLAnchorElement>('.test-campaign-link')?.href
),
testContext.driver.sourcegraphBaseUrl + '/users/alice/campaigns/campaign123'
)
})
it('lists user campaigns', async () => {
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses('user'),
CampaignsByUser: () => ({
node: {
__typename: 'User',
campaigns: {
nodes: [campaignListNode],
pageInfo: {
endCursor: null,
hasNextPage: false,
},
totalCount: 1,
},
},
}),
})
await driver.page.goto(driver.sourcegraphBaseUrl + '/users/alice/campaigns')
await driver.page.waitForSelector('.test-campaign-list-page')
await driver.page.waitForSelector('.test-campaign-link')
assert.strictEqual(
await driver.page.evaluate(
() => document.querySelector<HTMLAnchorElement>('.test-campaign-link')?.href
),
testContext.driver.sourcegraphBaseUrl + '/users/alice/campaigns/campaign123'
)
assert.strictEqual(await driver.page.$('.test-campaign-namespace-link'), null)
})
it('lists org campaigns', async () => {
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses('org'),
CampaignsByOrg: () => ({
node: {
__typename: 'Org',
campaigns: {
nodes: [
{
...campaignListNode,
url: '/organizations/test-org/campaigns/campaign123',
namespace: {
namespaceName: 'test-org',
url: '/organizations/test-org',
},
},
],
pageInfo: {
endCursor: null,
hasNextPage: false,
},
totalCount: 1,
},
},
}),
})
await driver.page.goto(driver.sourcegraphBaseUrl + '/campaigns')
await driver.page.goto(driver.sourcegraphBaseUrl + '/organizations/test-org/campaigns')
await driver.page.waitForSelector('.test-campaign-list-page')
await driver.page.waitForSelector('.test-campaign-link')
assert.strictEqual(
await driver.page.evaluate(
() => document.querySelector<HTMLAnchorElement>('.test-campaign-link')?.href
),
testContext.driver.sourcegraphBaseUrl + '/organizations/test-org/campaigns/campaign123'
)
assert.strictEqual(await driver.page.$('.test-campaign-namespace-link'), null)
})
})
describe('Campaign details', () => {
for (const entityType of ['user', 'org'] as const) {
it(`displays a single campaign for ${entityType}`, async () => {
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses(entityType),
CampaignChangesets,
ChangesetCountsOverTime,
ExternalChangesetFileDiffs,
})
const namespaceURL = entityType === 'user' ? '/users/alice' : '/organizations/test-org'
await driver.page.goto(driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123')
// View overview page.
await driver.page.waitForSelector('.test-campaign-details-page')
// Expand one changeset.
await driver.page.click('.test-campaigns-expand-changeset')
// Expect one diff to be rendered.
await driver.page.waitForSelector('.test-file-diff-node')
// Switch to view burndown chart.
await driver.page.click('.test-campaigns-chart-tab')
await driver.page.waitForSelector('.test-campaigns-chart')
// Go to close page via button.
await Promise.all([driver.page.waitForNavigation(), driver.page.click('.test-campaigns-close-btn')])
assert.strictEqual(
await driver.page.evaluate(() => window.location.href),
testContext.driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123/close'
)
await driver.page.waitForSelector('.test-campaign-close-page')
// Change overrides to make campaign appear closed.
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses(entityType, { closedAt: subDays(new Date(), 1).toISOString() }),
CampaignChangesets,
ChangesetCountsOverTime,
ExternalChangesetFileDiffs,
DeleteCampaign: () => ({
deleteCampaign: {
alwaysNil: null,
},
}),
})
// Return to details page.
await Promise.all([
driver.page.waitForNavigation(),
driver.page.click('.test-campaigns-close-abort-btn'),
])
await driver.page.waitForSelector('.test-campaign-details-page')
assert.strictEqual(
await driver.page.evaluate(() => window.location.href),
testContext.driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123'
)
// Delete the closed campaign.
await Promise.all([
driver.page.waitForNavigation(),
driver.acceptNextDialog(),
driver.page.click('.test-campaigns-delete-btn'),
])
assert.strictEqual(
await driver.page.evaluate(() => window.location.href),
testContext.driver.sourcegraphBaseUrl + namespaceURL + '/campaigns'
)
})
}
})
describe('Campaign spec preview', () => {
for (const entityType of ['user', 'org'] as const) {
it(`displays a preview of a campaign spec in ${entityType} namespace`, async () => {
const namespaceURL = entityType === 'user' ? '/users/alice' : '/organizations/test-org'
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses(entityType),
CampaignSpecByID: () => ({
node: {
__typename: 'CampaignSpec',
id: 'spec123',
appliesToCampaign: null,
createdAt: subDays(new Date(), 2).toISOString(),
creator: {
username: 'alice',
url: '/users/alice',
avatarURL: null,
},
description: {
name: 'test-campaign',
description: '### Very great campaign',
},
diffStat: {
added: 1000,
changed: 100,
deleted: 182,
},
expiresAt: addDays(new Date(), 3).toISOString(),
namespace:
entityType === 'user'
? {
namespaceName: 'alice',
url: '/users/alice',
}
: {
namespaceName: 'test-org',
url: '/organizations/test-org',
},
viewerCanAdminister: true,
},
}),
CampaignSpecChangesetSpecs: () => ({
node: {
__typename: 'CampaignSpec',
changesetSpecs: {
nodes: [
{
__typename: 'VisibleChangesetSpec',
description: {
__typename: 'GitBranchChangesetDescription',
baseRef: 'main',
headRef: 'head-ref',
baseRepository: {
name: 'github.com/sourcegraph/repo',
url: 'http://test.test/repo',
},
published: true,
body: 'Body',
commits: [
{
message: 'Commit message',
},
],
diffStat: {
added: 10,
changed: 2,
deleted: 9,
},
title: 'Changeset title',
},
expiresAt: addDays(new Date(), 3).toISOString(),
id: 'changesetspec123',
type: ChangesetSpecType.BRANCH,
},
],
pageInfo: {
endCursor: null,
hasNextPage: false,
},
totalCount: 1,
},
},
}),
ChangesetSpecFileDiffs: () => ({
node: {
__typename: 'VisibleChangesetSpec',
description: {
__typename: 'GitBranchChangesetDescription',
diff: mockDiff,
},
},
}),
CreateCampaign: () => ({
createCampaign: {
id: 'campaign123',
url: namespaceURL + '/campaigns/campaign123',
},
}),
})
await driver.page.goto(driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/apply/spec123')
// View overview page.
await driver.page.waitForSelector('.test-campaign-apply-page')
// Expand one changeset.
await driver.page.click('.test-campaigns-expand-changeset-spec')
// Expect one diff to be rendered.
await driver.page.waitForSelector('.test-file-diff-node')
// Apply campaign.
await Promise.all([
driver.page.waitForNavigation(),
driver.acceptNextDialog(),
driver.page.click('.test-campaigns-confirm-apply-btn'),
])
// Expect to be back at campaign overview page.
assert.strictEqual(
await driver.page.evaluate(() => window.location.href),
testContext.driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123'
)
})
}
})
describe('Campaign close preview', () => {
for (const entityType of ['user', 'org'] as const) {
it(`displays a preview for closing a campaign in ${entityType} namespace`, async () => {
testContext.overrideGraphQL({
...commonWebGraphQlResults,
...mockCommonGraphQLResponses(entityType),
CampaignChangesets,
ExternalChangesetFileDiffs,
CloseCampaign: () => ({
closeCampaign: {
id: 'campaign123',
},
}),
})
const namespaceURL = entityType === 'user' ? '/users/alice' : '/organizations/test-org'
await driver.page.goto(driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123/close')
// View overview page.
await driver.page.waitForSelector('.test-campaign-close-page')
// Check close changesets box.
assert.strictEqual(await driver.page.$('.test-campaigns-close-willclose-header'), null)
await driver.page.click('.test-campaigns-close-changesets-checkbox')
await driver.page.waitForSelector('.test-campaigns-close-willclose-header')
// Expand one changeset.
await driver.page.click('.test-campaigns-expand-changeset')
// Expect one diff to be rendered.
await driver.page.waitForSelector('.test-file-diff-node')
// Close campaign.
await Promise.all([
driver.page.waitForNavigation(),
driver.page.click('.test-campaigns-confirm-close-btn'),
])
// Expect to be back at campaign overview page.
assert.strictEqual(
await driver.page.evaluate(() => window.location.href),
testContext.driver.sourcegraphBaseUrl + namespaceURL + '/campaigns/campaign123'
)
})
}
})
})