Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added tests for bucket class creation flow
- Loading branch information
Showing
6 changed files
with
265 additions
and
9 deletions.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
frontend/packages/noobaa-storage-plugin/integration-tests/tests/bucketClassTest.scenario.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { execSync } from 'child_process'; | ||
import { $, browser, ExpectedConditions as until } from 'protractor'; | ||
import { goToInstalledOperators } from '@console/ceph-storage-plugin/integration-tests/views/add-capacity.view'; | ||
import * as crudView from '@console/internal-integration-tests/views/crud.view'; | ||
import { BucketClassHandler, createBucketClassLink, Tier } from '../views/createBC.view'; | ||
import { click } from '@console/shared/src/test-utils/utils'; | ||
import { appHost, testName } from '@console/internal-integration-tests/protractor.conf'; | ||
import { NS } from '@console/ceph-storage-plugin/integration-tests/utils/consts'; | ||
import { ocsOperator } from '../views/createBS.view'; | ||
|
||
const bcName = `${testName}-bucketclass`; | ||
const description = `${testName}-bucketClass is a bucket class being used for testing purposes. Please do not use it for real storage purposes in case the test fails and the class is not deleted`; | ||
|
||
const getBucketClassObject = () => | ||
JSON.parse(execSync(`kubectl get bucketclass ${bcName} -n openshift-storage -o json`).toString()); | ||
|
||
describe('Tests creation of Bucket Class', () => { | ||
const bcHandler = new BucketClassHandler(bcName, description); | ||
beforeAll(async () => { | ||
await browser.get(`${appHost}/ns/${NS}`); | ||
}); | ||
|
||
beforeEach(async () => { | ||
await goToInstalledOperators(); | ||
await browser.wait(until.and(crudView.untilNoLoadersPresent)); | ||
await browser.wait(until.visibilityOf(ocsOperator)); | ||
await ocsOperator.click(); | ||
await browser.wait(until.visibilityOf(createBucketClassLink)); | ||
await click(createBucketClassLink); | ||
}); | ||
|
||
afterEach(async () => { | ||
await crudView.clickDetailsPageAction('Delete Bucket Class'); | ||
await browser.wait(until.presenceOf($('#confirm-action'))); | ||
await $('#confirm-action').click(); | ||
await browser.sleep(100); | ||
}); | ||
|
||
it('Create a 1 Tier(Spread) Bucket Class', async () => { | ||
bcHandler.setTiers([Tier.SPREAD]); | ||
const bc = await bcHandler.createBC(); | ||
expect(bc.name).toEqual(bcName); | ||
expect(bc.namespace).toContain(NS); | ||
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD); | ||
const cliData = getBucketClassObject(); | ||
expect(cliData?.metadata?.name).toEqual(bc.name); | ||
}); | ||
|
||
it('Create a 1 Tier(Mirror) Bucket Class', async () => { | ||
bcHandler.setTiers([Tier.MIRROR]); | ||
const bc = await bcHandler.createBC(); | ||
expect(bc.name).toEqual(bcName); | ||
expect(bc.namespace).toContain(NS); | ||
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.MIRROR); | ||
const cliData = getBucketClassObject(); | ||
expect(cliData?.metadata?.name).toEqual(bc.name); | ||
}); | ||
|
||
it('Create a 2 Tier(Spread, Spread) Bucket Class', async () => { | ||
bcHandler.setTiers([Tier.SPREAD, Tier.SPREAD]); | ||
const bc = await bcHandler.createBC(); | ||
expect(bc.name).toEqual(bcName); | ||
expect(bc.namespace).toContain(NS); | ||
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD); | ||
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.SPREAD); | ||
const cliData = getBucketClassObject(); | ||
expect(cliData?.metadata?.name).toEqual(bcName); | ||
}); | ||
|
||
it('Create a 2 Tier(Spread, Mirror) Bucket Class', async () => { | ||
bcHandler.setTiers([Tier.SPREAD, Tier.MIRROR]); | ||
const bc = await bcHandler.createBC(); | ||
expect(bc.name).toEqual(bcName); | ||
expect(bc.namespace).toContain(NS); | ||
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD); | ||
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.MIRROR); | ||
const cliData = getBucketClassObject(); | ||
expect(cliData?.metadata?.name).toEqual(bcName); | ||
}); | ||
|
||
it('Create a 2 Tier(Mirror, Mirror) Bucket Class', async () => { | ||
bcHandler.setTiers([Tier.MIRROR, Tier.MIRROR]); | ||
const bc = await bcHandler.createBC(); | ||
expect(bc.name).toEqual(bcName); | ||
expect(bc.namespace).toContain(NS); | ||
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.MIRROR); | ||
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.MIRROR); | ||
const cliData = getBucketClassObject(); | ||
expect(cliData?.metadata?.name).toEqual(bcName); | ||
}); | ||
}); |
160 changes: 160 additions & 0 deletions
160
frontend/packages/noobaa-storage-plugin/integration-tests/views/createBC.view.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { execSync } from 'child_process'; | ||
import * as _ from 'lodash'; | ||
import { $, $$, browser, by, element, ExpectedConditions as until } from 'protractor'; | ||
import * as crudView from '@console/internal-integration-tests/views/crud.view'; | ||
import { click } from '@console/shared/src/test-utils/utils'; | ||
import { testName } from '@console/internal-integration-tests/protractor.conf'; | ||
|
||
// Backing Store is the third one in API details page | ||
export const createBucketClassLink = $('article:nth-child(3) a'); | ||
const tier1TableCheckBoxes = $$('table[aria-label="Select Backing Store for Tier 1"] input'); | ||
const tier2TableCheckBoxes = $$('table[aria-label="Select Backing Store for Tier 2"] input'); | ||
|
||
// Wizard Controls | ||
const nextButton = element(by.buttonText('Next')); | ||
const createBucketClassButton = element(by.buttonText('Create Bucket Class')); | ||
const bucketClassNameInput = $('input[aria-label="Bucket Class Name"]'); | ||
const bucketClassDescriptionInput = $('textarea[aria-label="Description of bucket class"]'); | ||
|
||
// Tier Buttons | ||
const tier1Spread = $('input#radio-1'); | ||
const tier1Mirror = $('input#radio-2'); | ||
const tier2Spread = $('input#radio-3'); | ||
const tier2Mirror = $('input#radio-4'); | ||
const addTierButton = $('button[data-testid="add-tier-btn"]'); | ||
|
||
// Review Page Data | ||
const bcName = $('p[data-testid="bc-name"]'); | ||
const bcNamespace = $('p[data-testid="namespace-value"]'); | ||
const bcDescription = $('p[data-testid="bc-desc"]'); | ||
const tier1Policy = $('h5[data-testid="tier1-policy"]'); | ||
const tier2Policy = $('h5[data-testid="tier2-policy"]'); | ||
|
||
export enum Tier { | ||
SPREAD = 'SPREAD', | ||
MIRROR = 'MIRROR', | ||
} | ||
|
||
const TierCountMap = Object.freeze({ | ||
[Tier.SPREAD]: 1, | ||
[Tier.MIRROR]: 2, | ||
}); | ||
|
||
const getBackingStoreRequiredCount = (tiers: Tier[]) => { | ||
let count: number = 0; | ||
count += TierCountMap[tiers[0]]; | ||
if (tiers.length > 1) count += TierCountMap[tiers[1]]; | ||
return count; | ||
}; | ||
|
||
const createDummyBackingStore = (name: string) => { | ||
const defaultBS = JSON.parse( | ||
execSync( | ||
'kubectl get backingstores.noobaa.io noobaa-default-backing-store -n openshift-storage -o json', | ||
).toString(), | ||
); | ||
const customObj = _.pick(defaultBS, ['apiVersion', 'kind', 'metadata', 'spec']); | ||
customObj.metadata = Object.assign(_.pick(customObj.metadata, ['name', 'namespace']), { | ||
name, | ||
}); | ||
execSync(`echo '${JSON.stringify(customObj)}' | kubectl create -f -`); | ||
}; | ||
|
||
const tierLevelToButton = (level: number, tier: Tier) => | ||
level === 1 | ||
? tier === Tier.SPREAD | ||
? tier1Spread | ||
: tier1Mirror | ||
: tier === Tier.SPREAD | ||
? tier2Spread | ||
: tier2Mirror; | ||
|
||
export class BucketClassHandler { | ||
name: string; | ||
|
||
namespace: string; | ||
|
||
description: string; | ||
|
||
tiers: Tier[]; | ||
|
||
constructor(name: string, description: string, namespace: string = 'openshift-storage') { | ||
this.name = name; | ||
this.namespace = namespace; | ||
this.description = description; | ||
// Create 3 backingStores similar to default backing store | ||
for (let count = 0; count < 3; count++) { | ||
createDummyBackingStore(`${testName}-${count}`); | ||
} | ||
} | ||
|
||
setTiers(tiers: Tier[]) { | ||
this.tiers = tiers; | ||
} | ||
|
||
async createBC() { | ||
// Select namespace | ||
await browser.wait(until.and(crudView.untilNoLoadersPresent)); | ||
// General Data Page | ||
await this.setGeneralData(); | ||
await click(nextButton); | ||
// Placement Policy Page | ||
await browser.wait(until.and(crudView.untilNoLoadersPresent)); | ||
await this.setPlacementPolicy(); | ||
await click(nextButton); | ||
// Backing Store Selection Page | ||
await browser.wait(until.and(crudView.untilNoLoadersPresent)); | ||
await this.setBackingStores(); | ||
await click(nextButton); | ||
// Review Page | ||
await browser.wait(until.and(crudView.untilNoLoadersPresent)); | ||
const data = await this.getReviewPageData(); | ||
await click(createBucketClassButton); | ||
return data; | ||
} | ||
|
||
async setGeneralData() { | ||
await bucketClassNameInput.sendKeys(this.name); | ||
await bucketClassDescriptionInput.sendKeys(this.name); | ||
} | ||
|
||
async setPlacementPolicy() { | ||
await click(tierLevelToButton(1, this.tiers[0])); | ||
if (this.tiers.length > 1) { | ||
await click(addTierButton); | ||
await click(tierLevelToButton(2, this.tiers[1])); | ||
} | ||
} | ||
|
||
async setBackingStores() { | ||
// Wait until the table gets populated | ||
await browser.wait( | ||
until.and( | ||
async () => | ||
getBackingStoreRequiredCount(this.tiers) <= (await tier1TableCheckBoxes.count()), | ||
), | ||
); | ||
// Select tier 1 Backing Stores | ||
await click(tier1TableCheckBoxes.get(0)); | ||
if (TierCountMap[this.tiers[0]] > 1) await click(tier1TableCheckBoxes.get(1)); | ||
// Select tier 2 Backing Stores | ||
if (this.tiers.length > 1) { | ||
await click(tier2TableCheckBoxes.get(0)); | ||
if (TierCountMap[this.tiers[1]] > 1) await click(tier2TableCheckBoxes.get(1)); | ||
} | ||
} | ||
|
||
async getReviewPageData() { | ||
const dump = { | ||
name: await bcName.getText(), | ||
namespace: await bcNamespace.getText(), | ||
description: await bcDescription.getText(), | ||
tier1Policy: await tier1Policy.getText(), | ||
tier2Policy: null, | ||
}; | ||
if (this.tiers.length > 1) { | ||
dump.tier2Policy = await tier2Policy.getText(); | ||
} | ||
return dump; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters