├── .gitignore ├── package.json ├── History.md ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nightmare-swiftly", 3 | "version": "0.2.4", 4 | "author": "Segment.io", 5 | "keywords": [ 6 | "nightmare", 7 | "phantomjs", 8 | "swiftly" 9 | ], 10 | "description": "A Nightmare plugin for Swiftly.com", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/segmentio/nightmare-swiftly.git" 14 | }, 15 | "dependencies": { 16 | "superagent": "~0.17.0" 17 | }, 18 | "devDependencies": {} 19 | } -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.2.4 / 2014-11-10 3 | ================== 4 | 5 | * fixing for new 99designs website merge 6 | 7 | 0.2.3 / 2014-10-26 8 | ================== 9 | 10 | * adding better wait for more robust downloads 11 | 12 | 0.2.2 / 2014-07-16 13 | ================== 14 | 15 | * fix for swiftly html change 16 | 17 | 0.2.1 / 2014-05-21 18 | ================== 19 | 20 | * change wait interval to one minute 21 | 22 | 0.2.0 / 2014-05-21 23 | ================== 24 | 25 | * rename `onState` to `wait` 26 | 27 | 0.1.0 / 2014-05-21 28 | ================== 29 | 30 | * add `state` method 31 | * fixes 32 | 33 | n.n.n / 2014-04-13 34 | ================== 35 | 36 | * adding gitignore and fixing downloads to preserve name 37 | 38 | 0.0.7 / 2014-04-13 39 | ================== 40 | 41 | * fixing downloading 42 | * fixing history 43 | 44 | 0.0.6 / 2014-04-13 45 | ================== 46 | 47 | * fixing url mapping and require exec 48 | * fixing history 49 | 50 | 0.0.5 / 2014-04-12 51 | ================== 52 | 53 | * viewport improves screenshots if you need em 54 | * fixing history 55 | 56 | 0.0.4 / 2014-04-11 57 | ================== 58 | 59 | * fixing download and .wait(fn) 60 | * updating readme with new macros 61 | * fixing history 62 | 63 | 0.0.3 / 2014-04-08 64 | ================== 65 | 66 | * adding several more macros 67 | * fixing history 68 | 69 | 0.0.2 / 2014-04-08 70 | ================== 71 | 72 | * adding task url and cleaning up naming 73 | * nvm upload is required 74 | * fixing null uploads case 75 | * fixing history 76 | 77 | 0.0.1 / 2014-04-07 78 | ================== 79 | 80 | * setting up package.json 81 | * adding in basic plugin 82 | * fixing url 83 | * fixing 84 | * updating readma/scaffolding 85 | * Initial commit 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nightmare-swiftly 2 | ================= 3 | 4 | [Nightmare](https://github.com/segmentio/nightmare) plugin for Swiftly.com 5 | 6 | ### .login(email, password) 7 | 8 | Login to your account. 9 | 10 | ### .task(instructions, uploads, callback) 11 | 12 | Create a new task, uploads is an array of string paths, must include at least one upload by Swiftly rules. The callback signature is `cb(taskUrl)`. 13 | 14 | ### .state(callback) 15 | 16 | Get the task's state. One of: `'pending'`, `'in progress'`, `'delivered'` or `'approved'`. 17 | 18 | ### .wait(state, callback) 19 | 20 | Lets you wait until a task hits a state. 21 | 22 | ### .download(taskUrl, path) 23 | 24 | Download all the files produced with the task to the `path`. 25 | 26 | ### .approve(taskUrl) 27 | 28 | Approve the completed task. 29 | 30 | ## License (MIT) 31 | 32 | ``` 33 | WWWWWW||WWWWWW 34 | W W W||W W W 35 | || 36 | ( OO )__________ 37 | / | \ 38 | /o o| MIT \ 39 | \___/||_||__||_|| * 40 | || || || || 41 | _||_|| _||_|| 42 | (__|__|(__|__| 43 | ``` 44 | 45 | Copyright (c) 2014 Segment.io Inc. 46 | 47 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | var exec = require('child_process').exec; 3 | var request = require('superagent'); 4 | var fs = require('fs'); 5 | 6 | /** 7 | * Login to a Swiftly account. 8 | * 9 | * @param {String} email 10 | * @param {String} password 11 | */ 12 | 13 | var login = exports.login = function(email, password){ 14 | return function(nightmare) { 15 | nightmare 16 | .viewport(800, 1600) 17 | .goto('http://99designs.com/login') 18 | .type('#username', email) 19 | .type('#password', password) 20 | .click('.button--primary') 21 | .wait(); 22 | }; 23 | }; 24 | 25 | /** 26 | * Execute an entire task on Swiftly. 27 | * 28 | * @param {String} instructions 29 | * @param {Array} uploads 30 | * @param {String} path 31 | */ 32 | 33 | var task = exports.task = function(instructions, uploads, path) { 34 | return function(nightmare) { 35 | nightmare 36 | .use(create(instructions, uploads)) 37 | .use(wait('delivered')) 38 | .use(download(path)) 39 | .use(approve()); 40 | }; 41 | }; 42 | 43 | /** 44 | * Create a new task on Swiftly. 45 | * 46 | * @param {String} instructions 47 | * @param {Array} uploads 48 | * @param {Function} callback 49 | */ 50 | 51 | var create = exports.create = function(instructions, uploads) { 52 | return function(nightmare) { 53 | nightmare 54 | .goto('https://99designs.com/tasks/create') 55 | .wait(2000) 56 | .type('#body', instructions); 57 | 58 | uploads.forEach(function(path) { 59 | nightmare.upload('input[type=file]', path); 60 | }); 61 | 62 | nightmare 63 | .wait(5000) 64 | .click('#task-pay-button') 65 | .wait(2000) 66 | .click('#pay-button') 67 | .wait() 68 | .wait(); 69 | }; 70 | }; 71 | 72 | /** 73 | * Get task state. One of: 74 | * 75 | * - 'pending' 76 | * - 'in progress' 77 | * - 'delivered' 78 | * - 'approved' 79 | * 80 | * @param {String} state 81 | */ 82 | 83 | var state = exports.state = function(fn) { 84 | return function(nightmare) { 85 | nightmare 86 | .evaluate(function() { 87 | var pill = document.querySelector('.pill'); 88 | return pill ? pill.textContent.toLowerCase() : 'pending'; 89 | }, fn); 90 | }; 91 | }; 92 | 93 | /** 94 | * Wait until a task has a certain `state`. 95 | 96 | * @param {String} state 97 | */ 98 | 99 | var wait = exports.wait = function(state) { 100 | var interval = 1000 * 30; // 30 seconds 101 | return function(nightmare) { 102 | nightmare 103 | .wait(function() { 104 | var pill = document.querySelector('.pill'); 105 | return pill ? pill.textContent.toLowerCase() : 'pending'; 106 | }, state.toLowerCase(), interval); 107 | }; 108 | }; 109 | 110 | /** 111 | * Download the results of a task. 112 | * 113 | * @param {String} path 114 | * @param {String} url 115 | */ 116 | 117 | var download = exports.download = function(path, url) { 118 | return function(nightmare){ 119 | if (url) nightmare.goto(url).wait('.attachment__actions__download'); 120 | nightmare 121 | .evaluate(function() { 122 | var deliveries = document.querySelectorAll('.delivery'); 123 | var delivery = deliveries[deliveries.length - 1]; 124 | var links = delivery.querySelectorAll('.attachment__actions__download'); 125 | var files = [].slice.call(links).map(function(link){ 126 | return { 127 | name: link.getAttribute('download'), 128 | url: link.getAttribute('href') 129 | }; 130 | }); 131 | return files; 132 | }, function(files) { 133 | files.forEach(function(file) { 134 | var stream = fs.createWriteStream(path + '/' + file.name); 135 | request.get(file.url).pipe(stream); 136 | }); 137 | }) 138 | .wait(3000); 139 | }; 140 | }; 141 | 142 | /** 143 | * Approve a task. 144 | * 145 | * @param {String} url 146 | */ 147 | 148 | var approve = exports.approve = function(url) { 149 | return function(nightmare) { 150 | if (url) nightmare.goto(url); 151 | nightmare 152 | .click('.task-actions .button--primary') 153 | .click('.face--good') 154 | .click('.approve-delivery .button--primary') 155 | .wait(); 156 | }; 157 | }; 158 | 159 | 160 | --------------------------------------------------------------------------------