├── .gitignore ├── Gemfile ├── Gemfile.lock ├── .pairs ├── .travis.yml ├── package.json ├── example ├── example-support.js ├── payment.js ├── stub-configuration.js └── index.html ├── LICENSE ├── test ├── karma.conf.js └── apple-pay-js-stubs-test.js ├── CODE_OF_CONDUCT.md ├── src └── apple-pay-js-stubs.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | #-*-ruby-*- 2 | source 'https://rubygems.org' 3 | 4 | gem 'pivotal_git_scripts' 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | pivotal_git_scripts (1.4.0) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | pivotal_git_scripts 11 | -------------------------------------------------------------------------------- /.pairs: -------------------------------------------------------------------------------- 1 | # .pairs - configuration for 'git pair' 2 | # to see current pairing status, try 'git about' 3 | pairs: 4 | # : [; ] 5 | gt: Glen Tregoning; glen.tregoning 6 | jn: Julia Nguyen; julia.nguyen 7 | dh: Derek Hsu; derek 8 | email: 9 | prefix: pair 10 | domain: indiegogo.com 11 | no_solo_prefix: true 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | addons: 5 | firefox: "48.0" 6 | before_install: 7 | - "export DISPLAY=:99.0" 8 | - "sh -e /etc/init.d/xvfb start" 9 | - "echo \"Extracting firefox and setting PATH variable...\"" 10 | - "tar -xjf /tmp/firefox-48.0.tar.bz2 --directory /tmp" 11 | - "export PATH=\"/tmp/firefox:$PATH\"" 12 | - "echo \"Using firefox version `firefox --version`\"" 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apple-pay-js-stubs", 3 | "version": "1.0.4", 4 | "description": "The Apple Pay JS Stubs provide a stubbed implementation of the ApplePay JS framework allowing you to acceptance test your Apple Pay for the Web code without requiring Safari, or an iPhone with iOS 10", 5 | "main": "src/apple-pay-js-stubs.js", 6 | "scripts": { 7 | "test": "node_modules/.bin/karma start test/karma.conf.js --single-run" 8 | }, 9 | "author": "Indiegogo Inc.", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "chai": "^3.5.0", 13 | "chai-as-promised": "^5.3.0", 14 | "karma": "^1.1.2", 15 | "karma-chai": "^0.1.0", 16 | "karma-chai-as-promised": "^0.1.2", 17 | "karma-chrome-launcher": "^1.0.1", 18 | "karma-firefox-launcher": "^1.0.0", 19 | "karma-mocha": "^1.1.1", 20 | "karma-sinon": "^1.0.5", 21 | "mocha": "^3.0.0", 22 | "sinon": "^1.17.5" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git@github.com:indiegogo/apple-pay-js-stubs.git" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/example-support.js: -------------------------------------------------------------------------------- 1 | function formatLog(text) { 2 | var timestamp = Math.round(new Date().getTime()/1000) 3 | return timestamp + ": " + text; 4 | } 5 | 6 | function logSessionInteraction(text) { 7 | $( "
" + formatLog(text) + "
" ).appendTo( "#interactions" ); 8 | } 9 | 10 | function logSessionError(text) { 11 | $( "
" + formatLog(text) + "
" ).appendTo( "#interactions" ); 12 | } 13 | 14 | // loadScript based on Answer by e-satis 15 | // from http://stackoverflow.com/a/950146/40444 16 | function loadScript(url, callback) 17 | { 18 | // Adding the script tag to the head as suggested before 19 | var head = document.getElementsByTagName('head')[0]; 20 | var script = document.createElement('script'); 21 | script.type = 'text/javascript'; 22 | script.src = url; 23 | 24 | // Then bind the event to the callback function. 25 | // There are several events for cross browser compatibility. 26 | script.onreadystatechange = callback; 27 | script.onload = callback; 28 | 29 | // Fire the loading 30 | head.appendChild(script); 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Indiegogo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/payment.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function () { 2 | configureApplePay(); 3 | }); 4 | 5 | function configureApplePay() { 6 | if (window.ApplePaySession) { 7 | ApplePaySession.canMakePaymentsWithActiveCard("com.example.e-commerce"). 8 | then(function (canMakePayments) { 9 | if (canMakePayments) { 10 | $(".apple-pay").show(); 11 | $(".no-apple-pay").hide(); 12 | } else { 13 | $(".apple-pay").hide(); 14 | $(".no-apple-pay").show(); 15 | } 16 | }); 17 | } 18 | } 19 | 20 | $( ".apple-pay" ).click(function() { 21 | var request = {}; 22 | var session = new ApplePaySession(1, request); 23 | session.onvalidatemerchant = function (event) { 24 | logSessionInteraction("Validate Merchant Requested"); 25 | var fakeMerchantSession = {}; 26 | session.completeMerchantValidation(fakeMerchantSession); 27 | } 28 | 29 | session.oncancel = function(event) { 30 | logSessionInteraction("Payment Cancelled"); 31 | } 32 | 33 | session.onshippingcontactselected = function(event) { 34 | logSessionInteraction("Shipping Contact Selected"); 35 | session.completeShippingContactSelection(123, [], {}, {}); 36 | }; 37 | 38 | session.onpaymentauthorized = function(event) { 39 | logSessionInteraction("Payment Authorized by User"); 40 | session.completePayment(); 41 | }; 42 | 43 | session.begin(); 44 | }); -------------------------------------------------------------------------------- /example/stub-configuration.js: -------------------------------------------------------------------------------- 1 | // Stub Configuration 2 | 3 | $( document ).ready(function () { 4 | $( ".stub-config").prop("disabled",true); 5 | }); 6 | 7 | function loadStubs() { 8 | loadScript("../src/apple-pay-js-stubs.js", function() { 9 | enableStubPaymentsForBrowser(); 10 | configureApplePay(); 11 | }); 12 | $ ( "#enable-stubbing").text("Stubs Loaded"); 13 | $ ( "#enable-stubbing").prop("disabled",true); 14 | $ ( ".stub-config").prop("disabled",false); 15 | 16 | $ ( "#stub-config-cancel").click(configureStubBehaviourCancel); 17 | $ ( "#stub-config-shipping-authorize").click(configureStubBehaviourShippingAuthorize); 18 | } 19 | 20 | function resetExample() { 21 | enableStubPaymentsForBrowser(); 22 | } 23 | 24 | function enableStubPaymentsForBrowser() { 25 | ApplePaySession.stubCanMakePaymentsWithActiveCard = true; 26 | ApplePaySession.stubExecuteAfterMerchantValidation = function (session) { 27 | logSessionError("Error: No stub behaviour selected - please choose a configuration above first!"); 28 | } 29 | } 30 | 31 | function configureStubBehaviourCancel() { 32 | ApplePaySession.stubExecuteAfterMerchantValidation = function (session) { 33 | // 1. Stub acts as if the user cancels the paysheet 34 | var event = {}; 35 | session.oncancel(event); 36 | 37 | resetExample(); 38 | }; 39 | } 40 | 41 | function configureStubBehaviourShippingAuthorize() { 42 | ApplePaySession.stubExecuteAfterMerchantValidation = function (session) { 43 | // 1. Stub acts as if the user selects a on a shipping address 44 | var event = {}; 45 | session.onshippingcontactselected(event); 46 | // 2. Stub acts as if the user authorizes the payment 47 | session.onpaymentauthorized(event); 48 | 49 | resetExample(); 50 | }; 51 | } 52 | 53 | $("#enable-stubbing").click(function () { 54 | loadStubs(); 55 | configureApplePay(); 56 | }); -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Aug 01 2016 09:35:18 GMT-0700 (PDT) 3 | 4 | module.exports = function(config) { 5 | var cfg = { 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '../', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['mocha', 'chai-as-promised', 'chai'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'src/*.js', 19 | 'test/*.js' 20 | ], 21 | 22 | 23 | // list of files to exclude 24 | exclude: [ 25 | ], 26 | 27 | 28 | // preprocess matching files before serving them to the browser 29 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 30 | preprocessors: { 31 | }, 32 | 33 | 34 | // test results reporter to use 35 | // possible values: 'dots', 'progress' 36 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 37 | reporters: ['dots'], 38 | 39 | 40 | // web server port 41 | port: 9876, 42 | 43 | 44 | // enable / disable colors in the output (reporters and logs) 45 | colors: true, 46 | 47 | 48 | // level of logging 49 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 50 | logLevel: config.LOG_INFO, 51 | 52 | 53 | // enable / disable watching file and executing tests whenever any file changes 54 | autoWatch: true, 55 | 56 | 57 | // start these browsers 58 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 59 | browsers: ['Firefox'], 60 | 61 | 62 | // Continuous Integration mode 63 | // if true, Karma captures browsers, runs the tests and exits 64 | singleRun: false, 65 | 66 | // Concurrency level 67 | // how many browser should be started simultaneous 68 | concurrency: Infinity, 69 | }; 70 | 71 | config.set(cfg); 72 | } 73 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | apple-pay-js-stubs Example 4 | 26 | 27 | 28 | 29 |

Amazing Product

30 |

This is a product you'd definitely pay $10 for.

31 |

Price: $10

32 | 33 | 34 |
35 |

apple-pay-js-stubs Example Console

36 | 37 |

Configure apple-pay-js-stubs

38 |
    39 |
  • 40 | 41 |
  • 42 |
  • 43 | 44 |
  • 45 |
46 |

Log of Stubbed Behaviour

47 |
48 |
49 | 50 |

Instructions

51 |

52 | Initially this page is loaded without apple-pay-js-stubs loaded. 53 | To try out apple-pay-js-stubs you can control the stub behaviour using the Example Console above. 54 |

55 | You can enable stubbing for a given configuration by: 56 |

    57 |
  1. Clicking 'Load apple-pay-js-stubs'
  2. 58 |
  3. Clicking your preferred configuration option
  4. 59 |
  5. then click the '🍎 Pay'
  6. 60 |
61 | The 'Log of Stubbed Behaviour' outputs the stubbed user behaviour as a result of your clck on the '🍎 Pay' button.
62 | Note: after every '🍎 Pay' press the stub behaviour will be cleared. So please reselect another option between presses. 63 |

64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at open-source@indiegogo.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /src/apple-pay-js-stubs.js: -------------------------------------------------------------------------------- 1 | // Based on solution by naomik here http://stackoverflow.com/a/24216547 2 | class Emitter { 3 | constructor() { 4 | var delegate = document.createDocumentFragment(); 5 | [ 6 | 'addEventListener', 7 | 'dispatchEvent', 8 | 'removeEventListener' 9 | ].forEach(f => 10 | this[f] = (...xs) => delegate[f](...xs) 11 | ) 12 | } 13 | } 14 | 15 | class ApplePaySessionStub extends Emitter { 16 | constructor(version, paymentRequest) { 17 | super(); 18 | this.version = version; 19 | this._request = paymentRequest; 20 | } 21 | 22 | // Static Stub configuration 23 | 24 | static get stubCanMakePayments() { 25 | return this._stubCanMakePayments; 26 | } 27 | 28 | static set stubCanMakePayments(value) { 29 | this._stubCanMakePayments = value; 30 | } 31 | 32 | static get stubCanMakePaymentsWithActiveCard() { 33 | return this._stubCanMakePaymentsWithActiveCard; 34 | } 35 | 36 | static set stubCanMakePaymentsWithActiveCard(value) { 37 | this._stubCanMakePaymentsWithActiveCard = value; 38 | } 39 | 40 | static set stubExecuteAfterMerchantValidation(callback) { 41 | this._stubExecuteAfterMerchantValidation = callback; 42 | } 43 | 44 | static get stubExecuteAfterMerchantValidation() { 45 | return this._stubExecuteAfterMerchantValidation; 46 | } 47 | 48 | // Static Apple Pay JS interface 49 | 50 | static canMakePayments() { 51 | return this._stubCanMakePayments; 52 | } 53 | 54 | static canMakePaymentsWithActiveCard(merchantIdentifier) { 55 | return Promise.resolve(this.stubCanMakePaymentsWithActiveCard); 56 | } 57 | 58 | static supportsVersion(version) { 59 | return true; 60 | } 61 | 62 | // Instance Apple Pay JS interface 63 | 64 | abort() {} 65 | 66 | begin() { 67 | let url = 'https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'; 68 | if (this._onvalidatemerchant) { 69 | this._onvalidatemerchant( 70 | {validationURL: url} 71 | ); 72 | } 73 | var event = new ApplePayValidateMerchantEvent(url); 74 | this.dispatchEvent(event); 75 | } 76 | 77 | completeMerchantValidation(merchantSession) { 78 | if (!ApplePaySession.stubExecuteAfterMerchantValidation) { 79 | throw "Error: No stubExecuteAfterMerchantValidation() callback set"; 80 | } 81 | ApplePaySession.stubExecuteAfterMerchantValidation(this); 82 | } 83 | 84 | completePayment(status) { } 85 | 86 | completePaymentMethodSelection(newTotal, newLineItems) { } 87 | 88 | completeShippingContactSelection(status, newShippingMethods, newTotal, newLineItems) { } 89 | 90 | completeShippingMethodSelection(status, newTotal, newLineItems) { } 91 | 92 | set onvalidatemerchant(value) { 93 | this._onvalidatemerchant = value; 94 | } 95 | 96 | // Stub helper methods 97 | 98 | get request() { 99 | return this._request; 100 | } 101 | 102 | } 103 | 104 | window.ApplePaySession = ApplePaySessionStub; 105 | 106 | class ApplePayPaymentAuthorizedEvent extends Event { 107 | constructor(payment) { 108 | super("paymentauthorized"); 109 | this._payment = payment; 110 | } 111 | 112 | get payment() { 113 | return this._payment; 114 | } 115 | } 116 | window.ApplePayPaymentAuthorizedEvent = ApplePayPaymentAuthorizedEvent; 117 | 118 | class ApplePayValidateMerchantEvent extends Event { 119 | constructor(validationURL) { 120 | super("validatemerchant"); 121 | this._validationURL = validationURL; 122 | } 123 | 124 | get validationURL() { 125 | return this._validationURL; 126 | } 127 | } 128 | window.ApplePayValidateMerchantEvent = ApplePayValidateMerchantEvent; 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apple-pay-js-stubs 2 | The Apple Pay JS Stubs provide a stubbed implementation of the ApplePay JS framework allowing you to acceptance test your Apple Pay for the Web code without requiring Safari, or an iPhone with iOS 10 3 | 4 | [![Build Status](https://travis-ci.org/indiegogo/apple-pay-js-stubs.svg?branch=master)](https://travis-ci.org/indiegogo/apple-pay-js-stubs) 5 | 6 | This stubbed implementation substitutes the ApplePay JS API normally provided by the Safari browser on iOS 10, and macOS Sierra with a stubbed javascript implementation. 7 | 8 | Unlike the offical API, apple-pay-js-stubs presents no paysheet or other visual feedback when called, instead it can be configured to executes the ApplePay JS callbacks based the test scenario pre-configured by *you* the developer. 9 | 10 | This approach allows you to simulate both the ApplePay paysheet and user behaviour quickly and easily without requiring a physical ApplePay capable device. 11 | 12 | # Requirements 13 | - ECMAScript 6 complient browser 14 | - Tested in: 15 | - Chrome 51.0.2704.103 (64-bit) 16 | - Firefox 48.0 17 | - Safari Version 9.1 (11601.5.17.1) 18 | 19 | # Installation and Usage 20 | 21 | ## Step 1: Install apple-pay-js-stubs 22 | ### Option a: Install using node 23 | ```bash 24 | npm install apple-pay-js-stubs 25 | ``` 26 | 27 | ### Option b: Manually by downloading 28 | 29 | Alternatively you can download the [apple-pay-js-stubs.js file here](https://github.com/indiegogo/apple-pay-js-stubs/blob/master/src/apple-pay-js-stubs.js) 30 | 31 | ## Step 2: Load Javascript file in your acceptance tests 32 | 33 | In order for apple-pay-js-stubs.js to be available when your acceptance tests run, you'll need to load the javascript file on the page in your website which normally interacts with the window.ApplePaySession object. 34 | 35 | **Note: You should only do this when you're running your automated acceptance tests to avoid conflicting with Safari in your production environment** 36 | 37 | How you go about this will depend on what language you are writing your acceptance tests in. 38 | 39 | For acceptance tests written in Ruby's RSpec you can load `apple-pay-js-stubs.js` after you visit your ApplePay supporting webpage as follows: 40 | ```ruby 41 | javascript = <<-JAVASCRIPT 42 | var js = document.createElement("script"); 43 | js.type = "text/javascript"; 44 | js.src = "/assets/test_support/apple-pay-js-stubs.js"; 45 | document.body.appendChild(js); 46 | JAVASCRIPT 47 | page.execute_script(javascript) 48 | ``` 49 | (where `/assets/test_support/` is the path on your website to the apple-pay-js-stubs.js file) 50 | 51 | Note: This approach will result in a delay in `window.ApplePaySession` being available which may not be the same as on the real ApplePay supporting browser. 52 | 53 | ## Step 3: Configure the Stubs for your current test 54 | 55 | Each test using ApplePay/apple-pay-js-stubs, will need to configure the ApplePaySessionStubs class (stored in window.ApplePaySession) before executing the test. This is how you configure the call backs to simulate the users, and ApplePay JS's normal behaviour for a specific scenario. 56 | 57 | You can configure this by running the following javascript code on you page before each test: 58 | 59 | ```javascript 60 | // Configure what a call to ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier) should result to 61 | ApplePaySession.stubCanMakePaymentsWithActiveCard = true; // ApplePaySession.canMakePaymentsWithActiveCard() returns promise resulting to true 62 | 63 | ApplePaySession.stubExecuteAfterMerchantValidation = function(session) { 64 | // Call callbacks on session to simulate ApplePay JS / user behaviour 65 | }; 66 | ``` 67 | 68 | # Example Configurations 69 | Some example apple-pay-js-stubs javascript configurations: 70 | 71 | ### User without ApplePay configured 72 | ```javascript 73 | ApplePaySession.stubCanMakePaymentsWithActiveCard = false; 74 | ``` 75 | 76 | ### User cancels payment after opening paysheet 77 | ```javascript 78 | ApplePaySession.stubCanMakePaymentsWithActiveCard = true; 79 | ApplePaySession.stubExecuteAfterMerchantValidation = function(session) { 80 | session.oncancel({}) 81 | }; 82 | ``` 83 | 84 | ### User selects a shipping address, then authorizes the payment 85 | ```javascript 86 | ApplePaySession.stubCanMakePaymentsWithActiveCard = true; 87 | ApplePaySession.stubExecuteAfterMerchantValidation = function (session) { 88 | var event = {}; 89 | // 1. Stub acts as if the user selects a on a shipping address 90 | session.onshippingcontactselected(event); 91 | // 2. Stub acts as if the user authorizes the payment 92 | session.onpaymentauthorized(event); 93 | }; 94 | ``` 95 | 96 | 97 | # apple-pay-js-stubs Demo / Example 98 | 99 | You can see an example web page which demonstrates some of the capabilities of the stubs in [/example/index.html](/example/index.html). 100 | 101 | A few things to note: 102 | * To get the demo to load, you'll need to clone the repository locally then open example/index.html in the Chrome browser. 103 | * This demonstrates the stubs through an interactive 'Console'. Typically usage will be driven from your acceptance tests instead of interactively. 104 | 105 | 106 | ## Code of Conduct 107 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 108 | -------------------------------------------------------------------------------- /test/apple-pay-js-stubs-test.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | describe('apple-pay-js-stubs', function() { 4 | describe("ApplePaySession", function () { 5 | var ApplePaySession = window.ApplePaySession; 6 | it("has window.ApplePaySession set", function () { 7 | expect(ApplePaySession).to.not.be.undefined; 8 | }); 9 | 10 | describe('constructor', function () { 11 | it('should construct a session object', function() { 12 | var session = new ApplePaySession(1, {}); 13 | expect(session).to.not.be.undefined; 14 | }); 15 | }); 16 | 17 | describe('static canMakePaymentsWithActiveCard()', function () { 18 | it('returns fulfilled promise with the configured mockCanMakePaymentsWithActiveCard value', function () { 19 | ApplePaySession.stubCanMakePaymentsWithActiveCard = false; 20 | ApplePaySession.canMakePaymentsWithActiveCard("com.fake.merchant.identifier").should.eventually.equal(false); 21 | 22 | ApplePaySession.stubCanMakePaymentsWithActiveCard = true; 23 | ApplePaySession.canMakePaymentsWithActiveCard("com.fake.merchant.identifier").should.eventually.equal(true); 24 | }); 25 | }); 26 | 27 | describe("supportsVersion(version)", function () { 28 | it("returns true", function () { 29 | expect(ApplePaySession.supportsVersion(123)).to.be.true; 30 | }); 31 | }); 32 | 33 | describe("abort()", function () { 34 | it("has implementation", function () { 35 | var session = new ApplePaySession(1, {}); 36 | expect(function() { 37 | session.abort(); 38 | }).to.not.throw(); 39 | }); 40 | }); 41 | 42 | describe('begin()', function () { 43 | it('calls session.onvalidatemerchant() with expected validationURL', function () { 44 | var session = new ApplePaySession(1, {}); 45 | var theEvent; 46 | session.onvalidatemerchant = function(event) { 47 | theEvent = event; 48 | }; 49 | session.begin(); 50 | expect(theEvent.validationURL).to.equal('https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'); 51 | }); 52 | 53 | it('triggers ApplePayValidateMerchantEvent (validatemerchant) with expected validationURL', function () { 54 | var session = new ApplePaySession(1, {}); 55 | var theEvent; 56 | session.addEventListener("validatemerchant", function(event){ 57 | theEvent = event; 58 | }); 59 | session.begin(); 60 | expect(theEvent.validationURL).to.equal('https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'); 61 | }); 62 | }); 63 | 64 | describe("canMakePayments()", function () { 65 | it("has implementation that returns true", function () { 66 | ApplePaySession.stubCanMakePayments = true; 67 | expect(ApplePaySession.canMakePayments()).to.be.true; 68 | }); 69 | 70 | it("has implementation that returns false", function () { 71 | ApplePaySession.stubCanMakePayments = false; 72 | expect(ApplePaySession.canMakePayments()).to.be.false; 73 | }); 74 | }); 75 | 76 | describe("completeMerchantValidation()", function() { 77 | var session; 78 | beforeEach(function () { 79 | session = new ApplePaySession(1, {}); 80 | }); 81 | 82 | it('throws exception when no ApplePaySession.stubExecuteAfterMerchantValidation() callback is set when completeMerchantValidation is called', function () { 83 | expect(function() { 84 | session.completeMerchantValidation({}); 85 | }).to.throw("Error: No stubExecuteAfterMerchantValidation() callback set"); 86 | }); 87 | 88 | it('does not throw an exception when ApplePaySession.stubExecuteAfterMerchantValidation() callback is set when completeMerchantValidation is called', function () { 89 | ApplePaySession.stubExecuteAfterMerchantValidation = function() {}; 90 | expect(function() { 91 | session.completeMerchantValidation({}); 92 | }).to.not.throw(); 93 | }); 94 | 95 | it('calls ApplePaySession.stubExecuteAfterMerchantValidation() with session when completeMerchantValidation is called', function() { 96 | var calledWithSession; 97 | ApplePaySession.stubExecuteAfterMerchantValidation = function(session) { 98 | calledWithSession = session; 99 | }; 100 | session.completeMerchantValidation({}); 101 | expect(calledWithSession).to.eq(session); 102 | }); 103 | 104 | it('retains ApplePaySession.stubExecuteAfterMerchantValidation() after completeMerchantValidation is called', function() { 105 | var calledWithSession; 106 | var executeAfterMerchantValidation = function(session) { 107 | calledWithSession = session; 108 | }; 109 | 110 | ApplePaySession.stubExecuteAfterMerchantValidation = executeAfterMerchantValidation; 111 | session.completeMerchantValidation({}); 112 | expect(ApplePaySession.stubExecuteAfterMerchantValidation).to.eq(executeAfterMerchantValidation); 113 | }); 114 | }); 115 | 116 | describe("completePayment(status)", function () { 117 | it("has implementation", function () { 118 | var session = new ApplePaySession(1, {}); 119 | expect(function() { 120 | session.completePayment(123); 121 | }).to.not.throw(); 122 | }); 123 | }); 124 | 125 | describe("completePaymentMethodSelection(newTotal, newLineItems)", function() { 126 | it("has implementation", function () { 127 | var session = new ApplePaySession(1, {}); 128 | var newTotal = {}; 129 | var newLineItems = []; 130 | expect(function() { 131 | session.completePaymentMethodSelection(newTotal, newLineItems); 132 | }).to.not.throw(); 133 | }); 134 | }); 135 | 136 | describe("completeShippingContactSelection(status, newShippingMethods, newTotal, newLineItems)", function() { 137 | it("has implementation", function () { 138 | var session = new ApplePaySession(1, {}); 139 | var newShippingMethods = []; 140 | var status = 123; 141 | var newTotal = {}; 142 | var newLineItems = []; 143 | expect(function() { 144 | session.completeShippingContactSelection(status, newShippingMethods, newTotal, newLineItems); 145 | }).to.not.throw(); 146 | }); 147 | }); 148 | 149 | describe("completeShippingMethodSelection(status, newTotal, newLineItems)", function() { 150 | it("has implementation", function () { 151 | var session = new ApplePaySession(1, {}); 152 | var status = 123; 153 | var newTotal = {}; 154 | var newLineItems = []; 155 | expect(function() { 156 | session.completeShippingMethodSelection(status, newTotal, newLineItems); 157 | }).to.not.throw(); 158 | }); 159 | }); 160 | 161 | describe("request()", function () { 162 | it ("returns payment request", function () { 163 | var theRequest = {with: "expected value"}; 164 | var session = new ApplePaySession(1, theRequest); 165 | expect(session.request).to.eq(theRequest); 166 | }); 167 | }); 168 | }); 169 | 170 | describe("ApplePayPaymentAuthorizedEvent", function () { 171 | var ApplePayPaymentAuthorizedEvent = window.ApplePayPaymentAuthorizedEvent; 172 | it("has window.ApplePayPaymentAuthorizedEvent set", function () { 173 | expect(ApplePayPaymentAuthorizedEvent).to.not.be.undefined; 174 | }); 175 | 176 | it("has 'paymentauthorized' event.type", function() { 177 | var event = new ApplePayPaymentAuthorizedEvent({}); 178 | expect(event.type).to.eq('paymentauthorized'); 179 | }); 180 | 181 | it("has payment property which returns constructor passed payment object", function() { 182 | var paymentObject = {aKey: "aValue"}; 183 | var event = new ApplePayPaymentAuthorizedEvent(paymentObject); 184 | expect(event.payment).to.eq(paymentObject); 185 | }); 186 | }); 187 | 188 | describe("ApplePayValidateMerchantEvent", function () { 189 | var ApplePayValidateMerchantEvent = window.ApplePayValidateMerchantEvent; 190 | it("has window.ApplePayValidateMerchantEvent set", function () { 191 | expect(ApplePayValidateMerchantEvent).to.not.be.undefined; 192 | }); 193 | 194 | it("has 'validatemerchant' event.type", function() { 195 | var event = new ApplePayValidateMerchantEvent({}); 196 | expect(event.type).to.eq('validatemerchant'); 197 | }); 198 | 199 | it("has payment property which returns constructor passed payment object", function() { 200 | var validationURL = "url"; 201 | var event = new ApplePayValidateMerchantEvent(validationURL); 202 | expect(event.validationURL).to.eq(validationURL); 203 | }); 204 | }); 205 | }); 206 | --------------------------------------------------------------------------------