Skip to content

Commit bb0681b

Browse files
authored
Merge pull request #2024 from WikiEducationFoundation/HeadlessChrome
Switch from Poltergeist to headless Chrome for Capybara feature specs This changes the Capyabara driver from Poltergeist (based on the unmaintained PhantomJS headless browser) to Selenium and chromedriver. A few behavior bugs surfaced with Chrome that needed to be fixed in the frontend javascript, but most of the changes are just small adjustments to the specs. Chromedriver also helped pinpoint and fix some of causes of flapping tests, so I've mostly removed the 'pending' status from those tests and cleaned up the tests to pass consistently. Chrome renders CSS more accurately than poltergeist, so bugs and workarounds for misplaced elements could be removed. Chromedriver doesn't support automatically failing tests when there are JavaScript errors, so a check for javascript errors after each feature spec had to be patched into rails_helper. The one outstanding issue is that there are several javascript warnings that print to the console during certain tests. These come from `react-dom` code, but I haven't been able to pinpoint the cause.
2 parents e37d123 + 48dadd3 commit bb0681b

22 files changed

Image for: 22 files changed
+143
-176
lines changed

‎.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ cache:
1212
- vendor
1313
- node_modules
1414
addons:
15+
chrome: stable
1516
apt:
1617
packages:
1718
- pandoc

‎Gemfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ group :development, :test do
110110
gem 'rubocop-rspec-focused', require: false
111111
gem 'rubocop-rspec', require: false
112112
gem 'timecop' # Test utility for setting the time
113-
gem 'poltergeist' # Capypara feature specs driven by PhantomJS
114113
gem 'factory_bot_rails' # Factory for creating ActiveRecord objects in tests
115114
end
116115

@@ -119,6 +118,8 @@ group :test do
119118
gem 'rake', '>= 11.0'
120119
gem 'capybara'
121120
gem 'capybara-screenshot'
121+
gem 'chromedriver-helper' # Capypara feature specs driven by headless Chrome
122+
gem 'selenium-webdriver' # Capypara feature specs driven by headless Chrome
122123
gem 'database_cleaner'
123124
gem 'webmock'
124125
gem 'vcr' # Saves external web requests and replays them in tests

‎Gemfile.lock

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ GEM
9292
annotate (2.7.4)
9393
activerecord (>= 3.2, < 6.0)
9494
rake (>= 10.4, < 13.0)
95+
archive-zip (0.11.0)
96+
io-like (~> 0.3.0)
9597
arel (9.0.0)
9698
ast (2.4.0)
9799
bcrypt (3.1.12)
@@ -138,10 +140,14 @@ GEM
138140
capybara (>= 1.0, < 4)
139141
launchy
140142
chartkick (3.0.1)
143+
childprocess (0.9.0)
144+
ffi (~> 1.0, >= 1.0.11)
141145
choice (0.2.0)
146+
chromedriver-helper (1.2.0)
147+
archive-zip (~> 0.10)
148+
nokogiri (~> 1.8)
142149
chronic (0.10.2)
143150
climate_control (0.2.0)
144-
cliver (0.3.2)
145151
coderay (1.1.2)
146152
concurrent-ruby (1.0.5)
147153
connection_pool (2.2.2)
@@ -232,6 +238,7 @@ GEM
232238
concurrent-ruby (~> 1.0)
233239
i18n-js (3.0.11)
234240
i18n (>= 0.6.6, < 2)
241+
io-like (0.3.0)
235242
jaro_winkler (1.5.1)
236243
jbuilder (2.7.0)
237244
activesupport (>= 4.2.0)
@@ -308,10 +315,6 @@ GEM
308315
parallel (1.12.1)
309316
parser (2.5.1.2)
310317
ast (~> 2.4.0)
311-
poltergeist (1.18.1)
312-
capybara (>= 2.1, < 4)
313-
cliver (~> 0.3.1)
314-
websocket-driver (>= 0.2.0)
315318
powerpack (0.1.2)
316319
premailer (1.11.1)
317320
addressable
@@ -428,7 +431,11 @@ GEM
428431
ruby_dep (1.5.0)
429432
ruby_parser (3.11.0)
430433
sexp_processor (~> 4.9)
434+
rubyzip (1.2.1)
431435
safe_yaml (1.0.4)
436+
selenium-webdriver (3.11.0)
437+
childprocess (~> 0.5)
438+
rubyzip (~> 1.2)
432439
sentimental (1.4.1)
433440
sentry-raven (2.7.4)
434441
faraday (>= 0.7.6, < 1.0)
@@ -523,6 +530,7 @@ DEPENDENCIES
523530
capybara
524531
capybara-screenshot
525532
chartkick
533+
chromedriver-helper
526534
connection_pool
527535
dalli
528536
database_cleaner
@@ -550,7 +558,6 @@ DEPENDENCIES
550558
paper_trail
551559
paperclip
552560
piwik_analytics!
553-
poltergeist
554561
premailer-rails
555562
pry-rails
556563
puma
@@ -570,6 +577,7 @@ DEPENDENCIES
570577
rubocop
571578
rubocop-rspec
572579
rubocop-rspec-focused
580+
selenium-webdriver
573581
sentimental
574582
sentry-raven
575583
shoulda-matchers

‎app/assets/javascripts/components/categories/add_category_button.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Select from 'react-select';
77
import { initiateConfirm } from '../../actions/confirm_actions';
88
import TextInput from '../common/text_input';
99
import Popover from '../common/popover.jsx';
10-
import PopoverExpandable from '../high_order/popover_expandable.jsx';
10+
import Expandable from '../high_order/expandable.jsx';
1111
import CourseUtils from '../../utils/course_utils.js';
1212

1313
const AddCategoryButton = createReactClass({
@@ -210,5 +210,5 @@ const AddCategoryButton = createReactClass({
210210
const mapDispatchToProps = { initiateConfirm };
211211

212212
export default connect(null, mapDispatchToProps)(
213-
PopoverExpandable(AddCategoryButton)
213+
Expandable(AddCategoryButton)
214214
);

‎app/assets/javascripts/components/overview/tag_editable.jsx

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -79,37 +79,34 @@ const TagEditable = createReactClass({
7979
);
8080
});
8181

82-
let tagSelect;
83-
if (this.props.availableTags.length > 0) {
84-
const availableOptions = this.props.availableTags.map(tag => {
85-
return { label: tag, value: tag };
86-
});
87-
const tagOptions = [...this.state.createdTagOption, ...availableOptions];
88-
let addTagButtonDisabled = true;
89-
if (this.state.selectedTag) {
90-
addTagButtonDisabled = false;
91-
}
92-
tagSelect = (
93-
<tr>
94-
<th>
95-
<div className="select-with-button">
96-
<Select.Creatable
97-
className="fixed-width"
98-
ref="tagSelect"
99-
name="tag"
100-
value={this.state.selectedTag}
101-
placeholder={I18n.t('courses.tag_select')}
102-
onChange={this.handleChangeTag}
103-
options={tagOptions}
104-
/>
105-
<button type="submit" className="button dark" disabled={addTagButtonDisabled} onClick={this.addTag}>
106-
Add
107-
</button>
108-
</div>
109-
</th>
110-
</tr>
111-
);
82+
const availableOptions = this.props.availableTags.map(tag => {
83+
return { label: tag, value: tag };
84+
});
85+
const tagOptions = [...this.state.createdTagOption, ...availableOptions];
86+
let addTagButtonDisabled = true;
87+
if (this.state.selectedTag) {
88+
addTagButtonDisabled = false;
11289
}
90+
const tagSelect = (
91+
<tr>
92+
<th>
93+
<div className="select-with-button">
94+
<Select.Creatable
95+
className="fixed-width"
96+
ref="tagSelect"
97+
name="tag"
98+
value={this.state.selectedTag}
99+
placeholder={I18n.t('courses.tag_select')}
100+
onChange={this.handleChangeTag}
101+
options={tagOptions}
102+
/>
103+
<button type="submit" className="button dark" disabled={addTagButtonDisabled} onClick={this.addTag}>
104+
Add
105+
</button>
106+
</div>
107+
</th>
108+
</tr>
109+
);
113110

114111
return (
115112
<div key="tags" className="pop__container tags open" onClick={this.stop}>
@@ -122,7 +119,6 @@ const TagEditable = createReactClass({
122119
</div>
123120
);
124121
}
125-
126122
});
127123

128124
const mapStateToProps = state => ({

‎app/assets/javascripts/utils/api.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ slide_id=${opts.slide_id}`,
416416
.fail(function (obj, status) {
417417
this.obj = obj;
418418
this.status = status;
419-
console.error('Couldn\'t save course!');
420419
RavenLogger.obj = this.obj;
421420
RavenLogger.status = this.status;
422421
Raven.captureMessage('saveCourse failed', {

‎spec/features/admin_role_spec.rb

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
describe 'Admin users', type: :feature, js: true do
66
before do
77
page.current_window.resize_to(1920, 1080)
8-
page.driver.browser.url_blacklist = ['https://wikiedu.org']
98
end
109

1110
before do
@@ -62,16 +61,13 @@
6261
describe 'visiting the dashboard' do
6362
it 'sees submitted courses awaiting approval' do
6463
visit root_path
65-
sleep 1
6664
expect(page).to have_content 'Submitted & Pending Approval'
6765
expect(page).to have_content 'My Submitted Course'
6866
end
6967
end
7068

7169
describe 'adding a course to a campaign' do
7270
it 'makes the course live' do
73-
pending 'This sometimes fails on travis.'
74-
7571
stub_oauth_edit
7672
stub_chat_channel_create_success
7773

@@ -82,14 +78,12 @@
8278
click_button 'Edit Details'
8379
within '#course_campaigns' do
8480
page.find('.button.border.plus').click
85-
find('div.Select').send_keys('Fall 2015', :enter)
86-
omniclick find('.pop button', visible: true)
81+
find('input').send_keys('Fall 2015', :enter)
82+
click_button 'Add'
8783
end
8884

8985
expect(page).to have_content 'Your course has been published'
9086
expect(page).not_to have_content 'This course has been submitted for approval by its creator'
91-
92-
pass_pending_spec
9387
end
9488
end
9589

@@ -117,15 +111,13 @@
117111

118112
describe 'adding a tag to a course' do
119113
it 'works' do
120-
pending 'This sometimes fails on travis.'
121-
122114
stub_token_request
123115
visit "/courses/#{Course.first.slug}"
124116
click_button('Edit Details')
125-
within '.tags' do
126-
page.find('.button.border.plus').click
127-
page.find('input').set 'My Tag'
128-
find('.pop button', visible: true).click
117+
within '.pop__container.tags' do
118+
click_button '+'
119+
find('input').send_keys('My Tag', :enter)
120+
click_button 'Add'
129121
end
130122

131123
sleep 1
@@ -134,28 +126,22 @@
134126

135127
# Add the same tag again
136128
click_button('Edit Details')
137-
within('div.tags') do
138-
page.find('.button.border.plus').click
139-
end
140-
page.find('section.overview input[placeholder="Tag"]').set 'My Tag'
141-
page.all('.pop button', visible: true)[1].click
129+
within '.pop__container.tags' do
130+
click_button '+'
131+
find('input').send_keys('My Tag', :enter)
132+
click_button 'Add'
142133

143-
# Delete the tag
144-
within('div.tags') do
134+
# Delete the tag
145135
click_button '-'
146136
end
147137
sleep 1
148138
visit "/courses/#{Course.first.slug}"
149139
expect(page).not_to have_content 'My Tag'
150-
151-
pass_pending_spec
152140
end
153141
end
154142

155143
describe 'linking a course to its Salesforce record' do
156144
it 'makes the Link to Salesforce button appear' do
157-
pending 'This sometimes fails on travis.'
158-
159145
stub_token_request
160146
expect_any_instance_of(Restforce::Data::Client).to receive(:update!).and_return(true)
161147

@@ -165,8 +151,6 @@
165151
end
166152
expect(page).to have_content 'Open in Salesforce'
167153
expect(Course.first.flags[:salesforce_id]).to eq('a0f1a011101Xyas')
168-
169-
pass_pending_spec
170154
end
171155
end
172156
end

‎spec/features/campaign_overview_spec.rb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ module ResetLocale
9090
# Number of students
9191
# one non-instructor student per course and one instructor-student per course
9292
student_count = campaign_course_count * 2
93-
stat_text = "#{student_count} \n#{I18n.t('courses.students')}"
93+
stat_text = "#{student_count}\n#{I18n.t('courses.students')}"
9494
expect(page.find('.stat-display')).to have_content stat_text
9595

9696
# Words added
@@ -114,7 +114,7 @@ module ResetLocale
114114

115115
it 'falls back when locale is not available' do
116116
visit "/campaigns/#{campaign.slug}/overview?locale=aa"
117-
expect(page.find('.stat-display')).to have_content "20 \nStudents"
117+
expect(page.find('.stat-display')).to have_content "20\nStudents"
118118
end
119119

120120
# TODO: Test somewhere that has access to the request.
@@ -196,8 +196,8 @@ module ResetLocale
196196
it 'shows an error for invalid dates' do
197197
find('.campaign-details .rails_editable-edit').click
198198
find('#use_dates').click
199-
fill_in('campaign_start', with: '2016-01-10')
200-
fill_in('campaign_end', with: 'Invalid date')
199+
fill_in('campaign_start', with: '2016-01-10'.to_date)
200+
# fill_in('campaign_end', with: 'Invalid date')
201201
find('.campaign-details .rails_editable-save').click
202202
expect(page).to have_content(I18n.t('error.invalid_date', key: 'End'))
203203
find('#campaign_end', visible: true) # field with the error should be visible
@@ -206,8 +206,8 @@ module ResetLocale
206206
it 'updates the date fields properly, and unsets if #use_dates is unchecked' do
207207
find('.campaign-details .rails_editable-edit').click
208208
find('#use_dates').click
209-
fill_in('campaign_start', with: '2016-01-10')
210-
fill_in('campaign_end', with: '2016-02-10')
209+
fill_in('campaign_start', with: '2016-01-10'.to_date)
210+
fill_in('campaign_end', with: '2016-02-10'.to_date)
211211
find('.campaign-details .rails_editable-save').click
212212
expect(campaign.reload.start).to eq(Time.new(2016, 1, 10, 0, 0, 0, 0))
213213
expect(campaign.end).to eq(Time.new(2016, 2, 10, 23, 59, 59, 0))
@@ -235,9 +235,7 @@ module ResetLocale
235235
it 'throws an error if you enter the wrong campaign title when trying to delete it' do
236236
wrong_title = 'Not the title of the campaign'
237237
accept_alert(with: /"#{wrong_title}"/) do
238-
accept_prompt(with: wrong_title) do
239-
find('.campaign-delete .button').click
240-
end
238+
find('.campaign-delete .button').click
241239
end
242240
end
243241
end

‎spec/features/campaigns_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
find('.create-campaign-button').click
5757
fill_in('campaign_title', with: 'My Campaign Test')
5858
find('#use_dates').click
59-
fill_in('campaign_start', with: '2016-01-10') # end date not supplied
59+
fill_in('campaign_start', with: '2016-01-10'.to_date) # end date not supplied
6060
find('.wizard__form .button__submit').click
6161
find('.wizard__panel', visible: true)
6262
expect(page).to have_content(I18n.t('error.invalid_date', key: 'End'))
@@ -69,8 +69,8 @@
6969
fill_in('campaign_title', with: title)
7070
fill_in('campaign_description', with: description)
7171
find('#use_dates').click
72-
fill_in('campaign_start', with: '2016-01-10')
73-
fill_in('campaign_end', with: '2016-02-10')
72+
fill_in('campaign_start', with: '2016-01-10'.to_date)
73+
fill_in('campaign_end', with: '2016-02-10'.to_date)
7474
find('.wizard__form .button__submit').click
7575
expect(Campaign.last.title).to eq(title)
7676
expect(Campaign.last.description).to eq(description)

0 commit comments

Image for: 0 commit comments
Comments
 (0)