added Protractor for end-to-end testing
This commit is contained in:
parent
0841d2bfb9
commit
31d518f3e1
6 changed files with 524 additions and 145 deletions
|
@ -0,0 +1,62 @@
|
|||
import { element, by, browser, $, ElementFinder, ExpectedConditions as until } from 'protractor';
|
||||
|
||||
|
||||
export class ManageTriggerViewObject {
|
||||
|
||||
public sections: {[name: string]: ElementFinder} = {
|
||||
namespace: $('linear-workflow-section[section-id=namespace]'),
|
||||
githostrepo: $('linear-workflow-section[section-id=repo][section-title="Select Repository"]'),
|
||||
customrepo: $('linear-workflow-section[section-id=repo][section-title="Git Repository"]'),
|
||||
triggeroptions: $('linear-workflow-section[section-id=triggeroptions]'),
|
||||
dockerfilelocation: $('linear-workflow-section[section-id=dockerfilelocation]'),
|
||||
contextlocation: $('linear-workflow-section[section-id=contextlocation]'),
|
||||
robot: $('linear-workflow-section[section-id=robot]'),
|
||||
verification: $('linear-workflow-section[section-id=verification]'),
|
||||
};
|
||||
|
||||
private customGitRepoInput: ElementFinder = element(by.model('$ctrl.buildSource'));
|
||||
private dockerfileLocationInput: ElementFinder = this.sections['dockerfilelocation'].$('input');
|
||||
private dockerfileLocationDropdownButton: ElementFinder = this.sections['dockerfilelocation'].$('button[data-toggle=dropdown');
|
||||
private dockerContextInput: ElementFinder = this.sections['contextlocation'].$('input');
|
||||
private dockerContextDropdownButton: ElementFinder = this.sections['contextlocation'].$('button[data-toggle=dropdown');
|
||||
private robotAccountOptions: ElementFinder = this.sections['robot'].element(by.repeater('$ctrl.orderedData.visibleEntries'));
|
||||
|
||||
public continue(): Promise<void> {
|
||||
return Promise.resolve(element(by.buttonText('Continue')).click());
|
||||
}
|
||||
|
||||
public enterRepositoryURL(url: string): Promise<void> {
|
||||
browser.wait(until.presenceOf(this.customGitRepoInput));
|
||||
this.customGitRepoInput.clear();
|
||||
|
||||
return Promise.resolve(this.customGitRepoInput.sendKeys(url));
|
||||
}
|
||||
|
||||
public enterDockerfileLocation(path: string): Promise<void> {
|
||||
browser.wait(until.presenceOf(this.dockerfileLocationInput));
|
||||
this.dockerfileLocationInput.clear();
|
||||
|
||||
return Promise.resolve(this.dockerfileLocationInput.sendKeys(path));
|
||||
}
|
||||
|
||||
public getDockerfileSuggestions(): Promise<string[]> {
|
||||
return Promise.resolve(this.dockerfileLocationDropdownButton.click())
|
||||
.then(() => element.all(by.repeater('$ctrl.paths')).map(result => result.getText()));
|
||||
}
|
||||
|
||||
public enterDockerContext(path: string): Promise<void> {
|
||||
browser.wait(until.presenceOf(this.dockerContextInput));
|
||||
this.dockerContextInput.clear();
|
||||
|
||||
return Promise.resolve(this.dockerContextInput.sendKeys(path));
|
||||
}
|
||||
|
||||
public getDockerContextSuggestions(): Promise<string[]> {
|
||||
return Promise.resolve(this.dockerContextDropdownButton.click())
|
||||
.then(() => element.all(by.repeater('$ctrl.contexts')).map(result => result.getText()));
|
||||
}
|
||||
|
||||
public selectRobotAccount(index: number): Promise<void> {
|
||||
return Promise.resolve(element.all(by.css('input[type=radio]')).get(index).click());
|
||||
}
|
||||
}
|
19
static/test/e2e/sanity.scenario.ts
Normal file
19
static/test/e2e/sanity.scenario.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { browser } from 'protractor';
|
||||
import { appHost } from '../protractor.conf';
|
||||
|
||||
|
||||
describe("sanity test", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
browser.get(appHost);
|
||||
});
|
||||
|
||||
it("loads home view with no AngularJS errors", () => {
|
||||
browser.manage().logs().get('browser')
|
||||
.then((browserLog: any) => {
|
||||
browserLog.forEach((log: any) => {
|
||||
expect(log.message).not.toContain("angular");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
155
static/test/e2e/trigger-creation.scenario.ts
Normal file
155
static/test/e2e/trigger-creation.scenario.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import { browser, element, by, $, $$ } from 'protractor';
|
||||
import { ManageTriggerViewObject } from '../../js/directives/ui/manage-trigger/manage-trigger.view-object';
|
||||
import { appHost } from '../protractor.conf';
|
||||
|
||||
|
||||
describe("Trigger Creation", () => {
|
||||
const username = 'devtable';
|
||||
const password = 'password';
|
||||
var manageTriggerView: ManageTriggerViewObject = new ManageTriggerViewObject();
|
||||
|
||||
beforeAll((done) => {
|
||||
browser.waitForAngularEnabled(false);
|
||||
|
||||
// Sign in
|
||||
browser.get(appHost);
|
||||
$$('a[href="/signin/"]').get(1).click();
|
||||
$('#signin-username').sendKeys(username);
|
||||
$('#signin-password').sendKeys(password);
|
||||
element(by.partialButtonText('Sign in')).click();
|
||||
browser.sleep(4000).then(() => done());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
browser.waitForAngularEnabled(true);
|
||||
// TODO(alecmerdler): Delete all created triggers
|
||||
});
|
||||
|
||||
describe("for custom git", () => {
|
||||
|
||||
beforeAll(() => {
|
||||
// Navigate to trigger setup
|
||||
browser.get(`${appHost}/repository/devtable/simple?tab=builds`)
|
||||
});
|
||||
|
||||
it("can select custom git repository push as a trigger option", (done) => {
|
||||
element(by.buttonText('Create Build Trigger')).click();
|
||||
element(by.linkText('Custom Git Repository Push')).click();
|
||||
browser.sleep(1000);
|
||||
done();
|
||||
});
|
||||
|
||||
it("shows custom git repository section first", () => {
|
||||
expect(manageTriggerView.sections['customrepo'].isDisplayed()).toBe(true);
|
||||
});
|
||||
|
||||
it("does not accept invalid custom git repository URL's", () => {
|
||||
manageTriggerView.continue()
|
||||
.then(() => fail('Should not accept empty input for repository URL'))
|
||||
.catch(() => manageTriggerView.enterRepositoryURL('git@some'))
|
||||
.then(() => manageTriggerView.continue())
|
||||
.then(() => fail('Should not accept invalid input for repository URL'))
|
||||
.catch(() => null);
|
||||
});
|
||||
|
||||
it("proceeds to Dockerfile location section when given valid URL", () => {
|
||||
manageTriggerView.enterRepositoryURL('git@somegit.com:someuser/somerepo.git');
|
||||
manageTriggerView.continue()
|
||||
.then(() => {
|
||||
expect(manageTriggerView.sections['dockerfilelocation'].isDisplayed()).toBe(true);
|
||||
})
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
|
||||
it("does not accept Dockerfile location that does not end with a filename", () => {
|
||||
manageTriggerView.enterDockerfileLocation('/')
|
||||
.then(() => manageTriggerView.continue())
|
||||
.then(() => fail('Should not accept Dockerfile location that does not end with a filename'))
|
||||
.catch(() => null);
|
||||
});
|
||||
|
||||
it("does not provide Dockerfile location suggestions", () => {
|
||||
manageTriggerView.getDockerfileSuggestions()
|
||||
.then((results) => {
|
||||
expect(results.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("proceeds to Docker context location section when given a valid Dockerfile location", () => {
|
||||
manageTriggerView.enterDockerfileLocation('/Dockerfile')
|
||||
.then(() => manageTriggerView.continue())
|
||||
.then(() => {
|
||||
expect(manageTriggerView.sections['contextlocation'].isDisplayed()).toBe(true);
|
||||
})
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
|
||||
it("does not accept invalid Docker context", () => {
|
||||
manageTriggerView.enterDockerContext('')
|
||||
.then(() => manageTriggerView.continue())
|
||||
.then(() => fail('Should not acccept invalid Docker context location'))
|
||||
.catch(() => null);
|
||||
});
|
||||
|
||||
it("provides suggestions for Docker context based on Dockerfile location", () => {
|
||||
manageTriggerView.getDockerContextSuggestions()
|
||||
.then((results) => {
|
||||
expect(results).toContain('/');
|
||||
});
|
||||
});
|
||||
|
||||
it("proceeds to robot selection section when given valid Docker context", () => {
|
||||
manageTriggerView.enterDockerContext('/')
|
||||
.then(() => manageTriggerView.continue())
|
||||
.then(() => {
|
||||
expect(manageTriggerView.sections['robot'].isDisplayed()).toBe(true);
|
||||
})
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
|
||||
it("allows selection of optional robot account", () => {
|
||||
manageTriggerView.selectRobotAccount(0)
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
|
||||
it("proceeds to verification section", () => {
|
||||
manageTriggerView.continue()
|
||||
.then(() => {
|
||||
expect(manageTriggerView.sections['verification'].isDisplayed()).toBe(true);
|
||||
})
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
|
||||
it("displays success message after creating the trigger", () => {
|
||||
manageTriggerView.continue()
|
||||
.then(() => {
|
||||
browser.sleep(2000);
|
||||
expect($('h3').getText()).toEqual('Trigger has been successfully activated');
|
||||
})
|
||||
.catch(reason => fail(reason));
|
||||
});
|
||||
});
|
||||
|
||||
describe("for githost", () => {
|
||||
|
||||
beforeAll(() => {
|
||||
// Navigate to trigger setup
|
||||
browser.get(`${appHost}/repository/devtable/simple?tab=builds`);
|
||||
});
|
||||
|
||||
it("can select GitHub repository push as a trigger option", () => {
|
||||
element(by.partialButtonText('Create Build Trigger')).click();
|
||||
element(by.linkText('GitHub Repository Push')).click();
|
||||
});
|
||||
|
||||
it("redirects to GitHub login page for granting authentication", () => {
|
||||
expect(browser.getCurrentUrl()).toContain('github.com');
|
||||
|
||||
// TODO: Which credentials do we use to login to GitHub?
|
||||
});
|
||||
|
||||
xit("shows namespace select section first", () => {
|
||||
expect(manageTriggerView.sections['namespace'].isDisplayed()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
66
static/test/protractor.conf.ts
Normal file
66
static/test/protractor.conf.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { Config, browser } from 'protractor';
|
||||
import * as request from 'request';
|
||||
|
||||
|
||||
/*
|
||||
* Use a set environment variable or default value for the app host.
|
||||
*/
|
||||
export const appHost: string = process.env.APP_HOST || 'http://localhost:5000';
|
||||
|
||||
|
||||
/**
|
||||
* Protractor is configured to run against a Selenium instance running locally on port 4444 and a Quay instance running
|
||||
* locally on port 5000.
|
||||
* Easiest method is running the Quay and Selenium containers:
|
||||
* $ docker run -d --net=host -v /dev/shm:/dev/shm selenium/standalone-chrome:3.4.0
|
||||
* $ docker run -d --net=host quay.io/quay/quay
|
||||
* $ yarn run e2e
|
||||
*/
|
||||
export const config: Config = {
|
||||
framework: 'jasmine',
|
||||
seleniumAddress: 'http://localhost:4444/wd/hub',
|
||||
// Uncomment to run tests against local Chrome instance
|
||||
// directConnect: true,
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: [
|
||||
'--disable-infobars'
|
||||
],
|
||||
prefs: {
|
||||
'profile.password_manager_enabled': false,
|
||||
'credentials_enable_service': false,
|
||||
'password_manager_enabled': false
|
||||
}
|
||||
}
|
||||
},
|
||||
onPrepare: () => {
|
||||
browser.driver.manage().window().maximize();
|
||||
|
||||
// Resolve promise when request returns HTTP 200
|
||||
return new Promise((resolve, reject) => {
|
||||
const pollServer = (success, failure) => {
|
||||
request(appHost, (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
console.log(`Successfully connected to server at ${appHost}`);
|
||||
success();
|
||||
} else {
|
||||
console.log(`Could not connect to server at ${appHost}`);
|
||||
setTimeout(() => {
|
||||
failure(success, failure);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
pollServer(resolve, pollServer);
|
||||
});
|
||||
},
|
||||
onComplete: () => {
|
||||
browser.close();
|
||||
},
|
||||
specs: [
|
||||
'./e2e/sanity.scenario.ts',
|
||||
'./e2e/trigger-creation.scenario.ts'
|
||||
],
|
||||
};
|
Reference in a new issue