Bug 1576966 - Convert filtering selenium tests to React Testing Library

This commit is contained in:
Cameron Dawson 2019-08-27 10:28:31 -07:00
Родитель f05196f36b
Коммит 56f2c5ca78
8 изменённых файлов: 290 добавлений и 232 удалений

Просмотреть файл

@ -1,35 +0,0 @@
import pytest
from pages.treeherder import Treeherder
@pytest.fixture
def commits(create_push, create_commit, test_repository):
commit = create_commit(create_push(test_repository))
create_commit(create_push(
test_repository, revision=commit.revision[::-1], author='bar@foo.com'))
def test_filter_jobs_by_author(base_url, selenium, commits):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.pushes) == 2)
# check authors are distinct
assert len(set(push.author for push in page.pushes)) == 2
author = page.pushes[-1].author
page.pushes[-1].filter_by_author()
page.wait.until(lambda _: len(page.pushes) == 1)
assert page.pushes[0].author == author
assert len(page.active_filters.filters) == 1
assert page.active_filters.filters[0].field == 'author:'
assert page.active_filters.filters[0].value == author.split('@')[0]
def test_clear_filter_jobs_by_author(base_url, selenium, commits):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.pushes) == 2)
page.pushes[0].filter_by_author()
page.wait.until(lambda _: len(page.pushes) == 1)
page.active_filters.filters[0].clear()
page.wait.until(lambda _: len(page.pushes) == 2)
# check authors are distinct
assert len(set(push.author for push in page.pushes)) == 2

Просмотреть файл

@ -1,28 +0,0 @@
import pytest
from pages.treeherder import Treeherder
RESULTS = ['testfailed', 'busted', 'exception']
@pytest.fixture
def test_jobs(eleven_job_blobs, create_jobs):
for i, status in enumerate(RESULTS):
eleven_job_blobs[i]['job']['result'] = status
return create_jobs(eleven_job_blobs[0:len(RESULTS)])
@pytest.mark.parametrize('result', RESULTS)
def test_filter_jobs_by_failure_result(base_url, selenium, test_jobs, result):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
assert len(page.all_jobs) == len(RESULTS)
with page.filters_menu() as filters:
for result in RESULTS:
getattr(filters, 'toggle_{}_jobs'.format(result))()
assert len(page.all_jobs) == 0
with page.filters_menu() as filters:
getattr(filters, 'toggle_{}_jobs'.format(result))()
assert len(page.all_jobs) == 1
page.all_jobs[0].click()
assert page.details_panel.job_details.result == result

Просмотреть файл

@ -1,40 +0,0 @@
import pytest
from pages.treeherder import Treeherder
@pytest.fixture
def test_jobs(eleven_job_blobs, create_jobs):
for i, platform in enumerate(['linux32', 'windowsxp']):
eleven_job_blobs[i]['job']['machine_platform']['platform'] = platform
return create_jobs(eleven_job_blobs[0:2])
@pytest.mark.parametrize('method', [('keyboard'), ('pointer')])
def test_filter_jobs_by_keywords(base_url, selenium, test_jobs, method):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
page.filter_by('linux', method)
page.wait.until(lambda _: len(page.all_jobs) == 1)
def test_filter_jobs_by_keywords_from_job_panel(base_url, selenium, test_jobs):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
page.all_jobs[0].click()
keywords = page.details_panel.job_details.keywords
page.details_panel.job_details.filter_by_keywords()
page.wait.until(lambda _: len(page.all_jobs) < len(test_jobs))
assert page.quick_filter_term == keywords.lower()
# See https://bugzilla.mozilla.org/show_bug.cgi?id=1575835 to re-enable
@pytest.mark.skip(reason="The test times out too often and prevents deployments")
@pytest.mark.parametrize('method', [('keyboard'), ('pointer')])
def test_clear_filter(base_url, selenium, test_jobs, method):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
page.filter_by('linux')
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.clear_filter(method)
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))

Просмотреть файл

@ -1,100 +0,0 @@
import pytest
from pages.treeherder import Treeherder
JOB_DATA = [
{'result': 'testfailed'},
{'result': 'success'},
{'result': 'retry'},
{'result': 'usercancel'},
{'result': 'superseded'},
{'result': 'unknown', 'state': 'running'}]
@pytest.fixture
def test_jobs(eleven_job_blobs, create_jobs):
job_blobs = [j for j in eleven_job_blobs if 'superseded' not in j]
for i, job in enumerate(JOB_DATA):
job_blobs[i]['job'].update(job)
return create_jobs(job_blobs[0:len(JOB_DATA)])
def test_default_filters(base_url, selenium, test_jobs):
"""Show superseded, and verify all test jobs are displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_superseded() # defaults to hidden
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
def test_filter_failures(base_url, selenium, test_jobs):
"""Hide all except failures and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_success()
page.toggle_retry()
page.toggle_usercancel()
page.toggle_in_progress()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'testfailed'
def test_filter_success(base_url, selenium, test_jobs):
"""Hide all except success and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_failures()
page.toggle_retry()
page.toggle_usercancel()
page.toggle_in_progress()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'success'
def test_filter_retry(base_url, selenium, test_jobs):
"""Hide all except verify and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_success()
page.toggle_failures()
page.toggle_usercancel()
page.toggle_in_progress()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'retry'
def test_filter_usercancel(base_url, selenium, test_jobs):
"""Hide all except usercancel and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_success()
page.toggle_failures()
page.toggle_retry()
page.toggle_in_progress()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'usercancel'
def test_filter_superseded(base_url, selenium, test_jobs):
"""Hide all except superseded and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_success()
page.toggle_failures()
page.toggle_retry()
page.toggle_usercancel()
page.toggle_superseded() # defaults to hidden
page.toggle_in_progress()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'superseded'
def test_filter_in_progress(base_url, selenium, test_jobs):
"""Hide all except in_progress and verify job is displayed."""
page = Treeherder(selenium, base_url).open()
page.toggle_success()
page.toggle_failures()
page.toggle_retry()
page.toggle_usercancel()
page.wait.until(lambda _: len(page.all_jobs) == 1)
page.all_jobs[0].click()
assert page.details_panel.job_details.result == 'unknown'

Просмотреть файл

@ -1,20 +0,0 @@
import pytest
from pages.treeherder import Treeherder
RESULTS = ['testfailed', 'busted', 'exception']
@pytest.fixture
def test_jobs(eleven_job_blobs, create_jobs):
results = ['success'] + RESULTS
for i, status in enumerate(results):
eleven_job_blobs[i]['job']['result'] = status
return create_jobs(eleven_job_blobs[0:len(results)])
def test_filter_unclassified_jobs(base_url, selenium, test_jobs):
page = Treeherder(selenium, base_url).open()
page.wait.until(lambda _: len(page.all_jobs) == len(test_jobs))
page.filter_unclassified_jobs()
assert len(page.all_jobs) == len(RESULTS)

Просмотреть файл

@ -0,0 +1,281 @@
import React from 'react';
import { fetchMock } from 'fetch-mock';
import {
render,
cleanup,
fireEvent,
waitForElement,
waitForElementToBeRemoved,
} from '@testing-library/react';
import App from '../../../ui/job-view/App';
import reposFixture from '../mock/repositories';
import pushListFixture from '../mock/push_list';
import { getApiUrl } from '../../../ui/helpers/url';
import {
getProjectUrl,
replaceLocation,
setUrlParam,
} from '../../../ui/helpers/location';
import jobListFixtureOne from '../mock/job_list/job_1';
import jobMap from '../mock/job_map';
describe('Filtering', () => {
const repoName = 'autoland';
beforeAll(() => {
fetchMock.get('/revision.txt', []);
fetchMock.get(getApiUrl('/repository/'), reposFixture);
fetchMock.get(getApiUrl('/user/'), []);
fetchMock.get(getApiUrl('/failureclassification/'), []);
// ``begin:`` here will match any fetch call to an API that begins with
// this string.
fetchMock.get('begin:https://treestatus.mozilla-releng.net/trees/', {
result: {
message_of_the_day: '',
reason: '',
status: 'open',
tree: repoName,
},
});
fetchMock.get(
getProjectUrl('/push/?full=true&count=10', repoName),
pushListFixture,
);
fetchMock.get(getProjectUrl('/push/?full=true&count=10', 'try'), {
results: [
{
id: 111111,
revision: '3333333333335143b8df3f4b3e9b504dfbc589a0',
author: 'whozat@gmail.com',
revision_count: 1,
push_timestamp: 1562867957,
repository_id: 4,
revisions: [
{
repository_id: 4,
revision: '3333333333335143b8df3f4b3e9b504dfbc589a0',
author: 'whozat <whozat@gmail.com>',
comments: 'didathing',
},
],
},
],
});
fetchMock.get(`begin:${getApiUrl('/jobs/')}`, jobListFixtureOne);
});
afterEach(() => {
cleanup();
replaceLocation({});
});
const jobCount = () => document.querySelectorAll('.job-btn').length;
describe('by author', () => {
beforeEach(() => {
fetchMock.get(
getProjectUrl('/push/?full=true&count=10&author=reviewbot', 'autoland'),
{
results: [
{
id: 111111,
revision: '3333333333335143b8df3f4b3e9b504dfbc589a0',
author: 'reviewbot',
revision_count: 1,
push_timestamp: 1562867957,
repository_id: 4,
revisions: [
{
repository_id: 4,
revision: '3333333333335143b8df3f4b3e9b504dfbc589a0',
author: 'reviewbot <reviewbot>',
comments: 'didathing',
},
],
},
],
},
);
});
test('should have 1 push', async () => {
const { getAllByText, getByTestId } = render(<App />);
// wait till the ``reviewbot`` authored push is shown before filtering.
await waitForElement(() => getAllByText('reviewbot'));
setUrlParam('author', 'reviewbot');
await waitForElementToBeRemoved(() => getByTestId('push-511138'));
const filteredPushes = await waitForElement(() =>
getAllByText('View Tests'),
);
expect(filteredPushes).toHaveLength(1);
setUrlParam('author', null);
await waitForElement(() => getAllByText('jarilvalenciano@gmail.com'));
const unFilteredPushes = await waitForElement(() =>
getAllByText('View Tests'),
);
expect(unFilteredPushes).toHaveLength(10);
});
});
describe('by failure result', () => {
beforeEach(() => {
// polling.. Return nothing...
fetchMock.get(
`begin:${getProjectUrl(
'/push/?full=true&count=11&push_timestamp__lte=',
'autoland',
)}`,
{
results: [],
},
);
});
test('should have 10 failures', async () => {
const { getAllByText, getByTitle, findAllByText } = render(<App />);
await waitForElement(() => findAllByText('B'));
const unclassifiedOnlyButton = getByTitle(
'Loaded failures / toggle filtering for unclassified failures',
);
fireEvent.click(unclassifiedOnlyButton);
// Since yaml is not an unclassified failure, making this call will
// ensure that the filtering has completed. Then we can get an accurate
// count of what's left.
await waitForElementToBeRemoved(() => getAllByText('yaml'));
// The api returns the same joblist for each push.
// 10 pushes with 2 failures each, but only 1 unclassified.
expect(jobCount()).toBe(10);
// undo the filtering and make sure we see all the jobs again
fireEvent.click(unclassifiedOnlyButton);
await waitForElement(() => getAllByText('yaml'));
expect(jobCount()).toBe(40);
});
});
describe('by keywords', () => {
beforeAll(() => {
fetchMock.get(
getProjectUrl('/jobs/259537372/', 'autoland'),
Object.values(jobMap)[0],
);
fetchMock.get(
getProjectUrl('/job-log-url/?job_id=259537372', 'autoland'),
[],
);
fetchMock.get(
getProjectUrl('/performance/data/?job_id=259537372', 'autoland'),
[],
);
fetchMock.get(getApiUrl('/jobdetail/?job_id=259537372'), { results: [] });
fetchMock.get(getProjectUrl('/note/?job_id=259537372', 'autoland'), []);
fetchMock.get(
getProjectUrl('/bug-job-map/?job_id=259537372', 'autoland'),
[],
);
fetchMock.get(
getProjectUrl('/jobs/259537372/bug_suggestions/', 'autoland'),
[],
);
fetchMock.get(
getProjectUrl('/jobs/259537372/text_log_steps/', 'autoland'),
[],
);
});
const setFilterText = (filterField, text) => {
fireEvent.click(filterField);
fireEvent.change(filterField, { target: { value: text } });
fireEvent.keyDown(filterField, { key: 'Enter' });
};
test('string "yaml" should have 10 jobs', async () => {
const { getAllByText, findAllByText } = render(<App />);
await waitForElement(() => findAllByText('B'));
const filterField = document.querySelector('#quick-filter');
setFilterText(filterField, 'yaml');
await waitForElementToBeRemoved(() => getAllByText('B'));
expect(jobCount()).toBe(10);
// undo the filtering and make sure we see all the jobs again
setFilterText(filterField, null);
await waitForElement(() => getAllByText('B'));
expect(jobCount()).toBe(40);
});
test('click signature should have 10 jobs', async () => {
const { getByText, getByTitle, findAllByText } = render(<App />);
const build = await waitForElement(() => findAllByText('B'));
fireEvent.mouseDown(build[0]);
const sigLink = await waitForElement(() => getByText('(sig)'));
expect(sigLink.getAttribute('href')).toBe(
'/#/jobs?repo=autoland&selectedJob=259537372&searchStr=2aa083621bb989d6acf1151667288d5fe9616178',
);
const keywordLink = await waitForElement(() =>
getByTitle('Filter jobs containing these keywords'),
);
expect(keywordLink.getAttribute('href')).toBe(
'/#/jobs?repo=autoland&selectedJob=259537372&searchStr=gecko%2Cdecision%2Ctask%2Copt%2Cgecko%2Cdecision%2Ctask%2C%28d%29',
);
});
});
describe('by result status', () => {
const clickFilterChicklet = color => {
fireEvent.click(document.querySelector(`.btn-${color}-filter-chicklet`));
};
test('uncheck success should leave 30 jobs', async () => {
const { getAllByText, findAllByText } = render(<App />);
await waitForElement(() => findAllByText('B'));
clickFilterChicklet('green');
await waitForElementToBeRemoved(() => getAllByText('D'));
expect(jobCount()).toBe(30);
// undo the filtering and make sure we see all the jobs again
clickFilterChicklet('green');
await waitForElement(() => getAllByText('D'));
expect(jobCount()).toBe(40);
});
test('uncheck failures should leave 20 jobs', async () => {
const { getAllByText, findAllByText } = render(<App />);
const symbolToRemove = 'B';
await waitForElement(() => findAllByText(symbolToRemove));
clickFilterChicklet('red');
await waitForElementToBeRemoved(() => getAllByText(symbolToRemove));
expect(jobCount()).toBe(20);
// undo the filtering and make sure we see all the jobs again
clickFilterChicklet('red');
await waitForElement(() => getAllByText(symbolToRemove));
expect(jobCount()).toBe(40);
});
test('uncheck in progress should leave 20 jobs', async () => {
const { getAllByText, findAllByText } = render(<App />);
const symbolToRemove = 'yaml';
await waitForElement(() => findAllByText('B'));
clickFilterChicklet('dkgray');
await waitForElementToBeRemoved(() => getAllByText(symbolToRemove));
expect(jobCount()).toBe(30);
// undo the filtering and make sure we see all the jobs again
clickFilterChicklet('dkgray');
await waitForElement(() => getAllByText(symbolToRemove));
expect(jobCount()).toBe(40);
});
});
});

Просмотреть файл

@ -250,7 +250,7 @@ describe('Pushes Redux store', () => {
});
test('should get new unclassified counts with recalculateUnclassifiedCounts', async () => {
setUrlParam('job_type_symbol', 'cpp');
setUrlParam('job_type_symbol', 'B');
const { data: jobList } = await JobModel.getList({ push_id: 1 });
const state = reducer(
@ -261,7 +261,7 @@ describe('Pushes Redux store', () => {
const reduced = reducer(state, { type: RECALCULATE_UNCLASSIFIED_COUNTS });
expect(Object.keys(reduced.jobMap)).toHaveLength(4);
expect(reduced.allUnclassifiedFailureCount).toEqual(2);
expect(reduced.allUnclassifiedFailureCount).toEqual(1);
expect(reduced.filteredUnclassifiedFailureCount).toEqual(1);
});

Просмотреть файл

@ -15,7 +15,7 @@
"102210fe594ee9b33d82058545b1ed14f4c8206e",
"gecko-decision",
"opt",
526443,
511138,
"success",
"2aa083621bb989d6acf1151667288d5fe9616178",
"completed",
@ -33,7 +33,7 @@
"32faaecac742100f7753f0c1d0aa0add01b4046b",
"android-4-0-armv7-api16",
"debug",
526443,
511138,
"busted",
"e2e58eb7f14af83019d3f8433c0642dde8082c4d",
"completed",
@ -51,15 +51,15 @@
"102210fe594ee9b33d82058545b1ed14f4c8206e",
"lint",
"opt",
526443,
"success",
511138,
"unknown",
"306fd1e8d922922cd171fa31f0d914300ff52228",
"completed",
"running",
1
],
[
2,
1,
4,
259539664,
"unknown",
"?",
@ -69,7 +69,7 @@
"32faaecac742100f7753f0c1d0aa0add01b4046b",
"android-hw-p2-8-0-arm7-api-16",
"debug",
526443,
511138,
"testfailed",
"6862263481704547b805af8ba944438e1a6e63e9",
"completed",