Commit 689e565d 689e565db7bc2a29d2e75b40c3c6fdb7276912f3 by Matt Bowman

refs #307 - add fillNames and fillSelectors functionality

1 parent 0c4adc21
...@@ -746,21 +746,24 @@ Casper.prototype.fetchText = function fetchText(selector) { ...@@ -746,21 +746,24 @@ Casper.prototype.fetchText = function fetchText(selector) {
746 /** 746 /**
747 * Fills a form with provided field values. 747 * Fills a form with provided field values.
748 * 748 *
749 * @param Casper casper A Casper instance
749 * @param String selector A DOM CSS3/XPath selector to the target form to fill 750 * @param String selector A DOM CSS3/XPath selector to the target form to fill
750 * @param Object vals Field values 751 * @param Object vals Field values
751 * @param Boolean submit Submit the form? 752 * @param Object options The fill settings (optional)
752 */ 753 */
753 Casper.prototype.fill = function fill(selector, vals, submit) { 754 function fillForm (casper, selector, vals, options) {
754 "use strict"; 755 "use strict";
755 this.checkStarted(); 756 var submit, selectorFunction;
756 submit = submit === true ? submit : false; 757 casper.checkStarted();
757 if (!utils.isObject(vals)) { 758
758 throw new CasperError("Form values must be provided as an object"); 759 selectorFunction = options.selectorFunction;
759 } 760 submit = options.submit === true ? options.submit : false;
760 this.emit('fill', selector, vals, submit); 761
761 var fillResults = this.evaluate(function _evaluate(selector, values) { 762 casper.emit('fill', selector, vals, options);
762 return __utils__.fill(selector, values); 763
763 }, selector, vals); 764 var fillResults = casper.evaluate(function _evaluate(selector, vals, selectorFunction) {
765 return __utils__.fill(selector, vals, selectorFunction);
766 }, selector, vals, selectorFunction);
764 if (!fillResults) { 767 if (!fillResults) {
765 throw new CasperError("Unable to fill form"); 768 throw new CasperError("Unable to fill form");
766 } else if (fillResults.errors.length > 0) { 769 } else if (fillResults.errors.length > 0) {
...@@ -770,7 +773,7 @@ Casper.prototype.fill = function fill(selector, vals, submit) { ...@@ -770,7 +773,7 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
770 // File uploads 773 // File uploads
771 if (fillResults.files && fillResults.files.length > 0) { 774 if (fillResults.files && fillResults.files.length > 0) {
772 if (utils.isObject(selector) && selector.type === 'xpath') { 775 if (utils.isObject(selector) && selector.type === 'xpath') {
773 this.warn('Filling file upload fields is currently not supported using ' + 776 casper.warn('Filling file upload fields is currently not supported using ' +
774 'XPath selectors; Please use a CSS selector instead.'); 777 'XPath selectors; Please use a CSS selector instead.');
775 } else { 778 } else {
776 (function _each(self) { 779 (function _each(self) {
...@@ -781,15 +784,15 @@ Casper.prototype.fill = function fill(selector, vals, submit) { ...@@ -781,15 +784,15 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
781 if (!fs.exists(file.path)) { 784 if (!fs.exists(file.path)) {
782 throw new CasperError('Cannot upload nonexistent file: ' + file.path); 785 throw new CasperError('Cannot upload nonexistent file: ' + file.path);
783 } 786 }
784 var fileFieldSelector = [selector, 'input[name="' + file.name + '"]'].join(' '); 787 var fileFieldSelector = selectorFunction(self, file.name, selector).fullSelector;
785 self.page.uploadFile(fileFieldSelector, file.path); 788 self.page.uploadFile(fileFieldSelector, file.path);
786 }); 789 });
787 })(this); 790 })(casper);
788 } 791 }
789 } 792 }
790 // Form submission? 793 // Form submission?
791 if (submit) { 794 if (submit) {
792 this.evaluate(function _evaluate(selector) { 795 casper.evaluate(function _evaluate(selector) {
793 var form = __utils__.findOne(selector); 796 var form = __utils__.findOne(selector);
794 var method = (form.getAttribute('method') || "GET").toUpperCase(); 797 var method = (form.getAttribute('method') || "GET").toUpperCase();
795 var action = form.getAttribute('action') || "unknown"; 798 var action = form.getAttribute('action') || "unknown";
...@@ -808,6 +811,55 @@ Casper.prototype.fill = function fill(selector, vals, submit) { ...@@ -808,6 +811,55 @@ Casper.prototype.fill = function fill(selector, vals, submit) {
808 } 811 }
809 }, selector); 812 }, selector);
810 } 813 }
814 }
815
816 /**
817 * Fills a form with provided field values using the Name attribute.
818 *
819 * @param String formSelector A DOM CSS3/XPath selector to the target form to fill
820 * @param Object vals Field values
821 * @param Boolean submit Submit the form?
822 */
823 Casper.prototype.fillNames = function fillNames(formSelector, vals, submit) {
824 "use strict";
825 return fillForm(this, formSelector, vals, {
826 submit: submit,
827 selectorFunction: function (self, selector, form) {
828 return {
829 fullSelector: [form, '[name="' + selector + '"]'].join(' '),
830 elts: (self.findAll ? self.findAll('[name="' + selector + '"]', form) : null)
831 };
832 }
833 });
834 };
835
836 /**
837 * Fills a form with provided field values using the Name attribute.
838 *
839 * @param String formSelector A DOM CSS3/XPath selector to the target form to fill
840 * @param Object vals Field values
841 * @param Boolean submit Submit the form?
842 */
843 Casper.prototype.fill = Casper.prototype.fillNames
844
845 /**
846 * Fills a form with provided field values using CSS3 selectors.
847 *
848 * @param String formSelector A DOM CSS3/XPath selector to the target form to fill
849 * @param Object vals Field values
850 * @param Boolean submit Submit the form?
851 */
852 Casper.prototype.fillSelectors = function fillSelectors(formSelector, vals, submit) {
853 "use strict";
854 return fillForm(this, formSelector, vals, {
855 submit: submit,
856 selectorFunction: function (self, selector, form) {
857 return {
858 fullSelector: [form, selector].join(' '),
859 elts: (self.findAll ? self.findAll(selector, form) : null)
860 };
861 }
862 });
811 }; 863 };
812 864
813 /** 865 /**
......
...@@ -194,19 +194,28 @@ ...@@ -194,19 +194,28 @@
194 }; 194 };
195 195
196 /** 196 /**
197 * Fills a form with provided field values, and optionnaly submits it. 197 * Fills a form with provided field values, and optionally submits it.
198 * 198 *
199 * @param HTMLElement|String form A form element, or a CSS3 selector to a form element 199 * @param HTMLElement|String form A form element, or a CSS3 selector to a form element
200 * @param Object vals Field values 200 * @param Object vals Field values
201 * @param Function findFunction A function to be used for getting the selector for the element or a list of matching elements (optional)
201 * @return Object An object containing setting result for each field, including file uploads 202 * @return Object An object containing setting result for each field, including file uploads
202 */ 203 */
203 this.fill = function fill(form, vals) { 204 this.fill = function fill(form, vals, findFunction) {
204 /*jshint maxcomplexity:8*/ 205 /*jshint maxcomplexity:8*/
205 var out = { 206 var out = {
206 errors: [], 207 errors: [],
207 fields: [], 208 fields: [],
208 files: [] 209 files: []
209 }; 210 };
211
212 findFunction = findFunction || function(self, name, form) {
213 return {
214 fullSelector: [form, '[name="' + name + '"]'].join(' '),
215 elts: self.findAll('[name="' + name + '"]', form)
216 };
217 };
218
210 if (!(form instanceof HTMLElement) || typeof form === "string") { 219 if (!(form instanceof HTMLElement) || typeof form === "string") {
211 this.log("attempting to fetch form element from selector: '" + form + "'", "info"); 220 this.log("attempting to fetch form element from selector: '" + form + "'", "info");
212 try { 221 try {
...@@ -226,7 +235,7 @@ ...@@ -226,7 +235,7 @@
226 if (!vals.hasOwnProperty(name)) { 235 if (!vals.hasOwnProperty(name)) {
227 continue; 236 continue;
228 } 237 }
229 var field = this.findAll('[name="' + name + '"]', form) || this.findAll('#' + name, form); 238 var field = findFunction(this, name, form).elts;
230 var value = vals[name]; 239 var value = vals[name];
231 if (!field || field.length === 0) { 240 if (!field || field.length === 0) {
232 out.errors.push('no field named "' + name + '" in form'); 241 out.errors.push('no field named "' + name + '" in form');
......
...@@ -59,6 +59,64 @@ casper.test.begin('fill() tests', 15, function(test) { ...@@ -59,6 +59,64 @@ casper.test.begin('fill() tests', 15, function(test) {
59 }); 59 });
60 }); 60 });
61 61
62 casper.test.begin('fillSelector() tests', 15, function(test) {
63 var fpath = fs.pathJoin(phantom.casperPath, 'README.md');
64
65 casper.start('tests/site/form.html', function() {
66 this.fillSelectors('form[action="result.html"]', {
67 "input[name='email']": 'chuck@norris.com',
68 "input[name='password']": 'chuck',
69 "textarea[name='content']": 'Am watching thou',
70 "input[name='check']": true,
71 "input[name='choice']": 'no',
72 "select[name='topic']": 'bar',
73 "input[name='file']": fpath,
74 "input[name='checklist[]']": ['1', '3']
75 }
76 );
77 test.assertEvalEquals(function() {
78 return __utils__.findOne('input[name="email"]').value;
79 }, 'chuck@norris.com', 'Casper.fill() can fill an input[type=text] form field');
80 test.assertEvalEquals(function() {
81 return __utils__.findOne('input[name="password"]').value;
82 }, 'chuck', 'Casper.fill() can fill an input[type=password] form field');
83 test.assertEvalEquals(function() {
84 return __utils__.findOne('textarea[name="content"]').value;
85 }, 'Am watching thou', 'Casper.fill() can fill a textarea form field');
86 test.assertEvalEquals(function() {
87 return __utils__.findOne('select[name="topic"]').value;
88 }, 'bar', 'Casper.fill() can pick a value from a select form field');
89 test.assertEvalEquals(function() {
90 return __utils__.findOne('input[name="check"]').checked;
91 }, true, 'Casper.fill() can check a form checkbox');
92 test.assertEvalEquals(function() {
93 return __utils__.findOne('input[name="choice"][value="no"]').checked;
94 }, true, 'Casper.fill() can check a form radio button 1/2');
95 test.assertEvalEquals(function() {
96 return __utils__.findOne('input[name="choice"][value="yes"]').checked;
97 }, false, 'Casper.fill() can check a form radio button 2/2');
98 test.assertEvalEquals(function() {
99 return __utils__.findOne('input[name="file"]').files.length === 1;
100 }, true, 'Casper.fill() can select a file to upload');
101 test.assertEvalEquals(function() {
102 return (__utils__.findOne('input[name="checklist[]"][value="1"]').checked &&
103 !__utils__.findOne('input[name="checklist[]"][value="2"]').checked &&
104 __utils__.findOne('input[name="checklist[]"][value="3"]').checked);
105 }, true, 'Casper.fill() can fill a list of checkboxes');
106 });
107 casper.thenClick('input[type="submit"]', function() {
108 test.assertUrlMatch(/email=chuck@norris.com/, 'Casper.fill() input[type=email] field was submitted');
109 test.assertUrlMatch(/password=chuck/, 'Casper.fill() input[type=password] field was submitted');
110 test.assertUrlMatch(/content=Am\+watching\+thou/, 'Casper.fill() textarea field was submitted');
111 test.assertUrlMatch(/check=on/, 'Casper.fill() input[type=checkbox] field was submitted');
112 test.assertUrlMatch(/choice=no/, 'Casper.fill() input[type=radio] field was submitted');
113 test.assertUrlMatch(/topic=bar/, 'Casper.fill() select field was submitted');
114 });
115 casper.run(function() {
116 test.done();
117 });
118 });
119
62 casper.test.begin('nonexistent fields', 1, function(test) { 120 casper.test.begin('nonexistent fields', 1, function(test) {
63 casper.start('tests/site/form.html', function() { 121 casper.start('tests/site/form.html', function() {
64 test.assertRaises(this.fill, ['form[action="result.html"]', { 122 test.assertRaises(this.fill, ['form[action="result.html"]', {
......