├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── dependabot.yml └── workflows │ ├── ci.yml │ └── codeql-analysis.yml ├── .gitignore ├── .jsdoc-config-type-def.json ├── .jsdoc-config.json ├── .npmignore ├── .nycrc.js ├── CHANGELOG.yaml ├── LICENSE.md ├── README.md ├── benchmark └── benchmark-results.json ├── codecov.yml ├── docs ├── concepts.md ├── mutation-tracking.md └── tutorial-jsdoc-config.json ├── examples ├── collection-v2.json ├── console-readout.js ├── digest.json ├── hawk.json ├── nested-v2-collection-without-name.json ├── nested-v2-collection.json └── oauth1.json ├── index.js ├── lib ├── collection │ ├── certificate-list.js │ ├── certificate.js │ ├── collection.js │ ├── cookie-list.js │ ├── cookie.js │ ├── description.js │ ├── event-list.js │ ├── event.js │ ├── form-param.js │ ├── header-list.js │ ├── header.js │ ├── item-group.js │ ├── item.js │ ├── mutation-tracker.js │ ├── property-base.js │ ├── property-list.js │ ├── property.js │ ├── proxy-config-list.js │ ├── proxy-config.js │ ├── query-param.js │ ├── request-auth.js │ ├── request-body.js │ ├── request.js │ ├── response.js │ ├── script.js │ ├── url.js │ ├── variable-list.js │ ├── variable-scope.js │ ├── variable.js │ └── version.js ├── content-info │ └── index.js ├── index.js ├── superstring │ ├── dynamic-variables.js │ └── index.js ├── url-pattern │ ├── url-match-pattern-list.js │ └── url-match-pattern.js └── util.js ├── npm ├── build-docs.js ├── build-types.js ├── create-release.js ├── publish-coverage.js ├── test-benchmark.js ├── test-browser.js ├── test-lint.js ├── test-system.js └── test-unit.js ├── package-lock.json ├── package.json ├── test ├── .eslintrc ├── benchmark │ └── variable-substitution.bench.js ├── fixtures │ ├── audio.mp3 │ ├── icon.png │ ├── index.js │ ├── japaneseCharacters.txt │ └── russianCharacters.txt ├── karma.conf.js ├── system │ ├── jsdoc-config.test.js │ ├── npm-publish.test.js │ ├── repository.test.js │ └── source-modules.test.js └── unit │ ├── certificate-list.test.js │ ├── certificate.test.js │ ├── collection.test.js │ ├── content-info.test.js │ ├── cookie-list.test.js │ ├── cookie.test.js │ ├── description.test.js │ ├── dynamic-variables.test.js │ ├── event-list.test.js │ ├── event.test.js │ ├── form-param.test.js │ ├── header-list.test.js │ ├── header.test.js │ ├── item-group.test.js │ ├── item.test.js │ ├── mutation-tracker.test.js │ ├── property-base.test.js │ ├── property-list.test.js │ ├── property.test.js │ ├── proxy-config-list.test.js │ ├── proxy-config.test.js │ ├── query-param.test.js │ ├── request-auth.test.js │ ├── request-body.test.js │ ├── request.test.js │ ├── response-mime.test.js │ ├── response.test.js │ ├── script.test.js │ ├── superstring.test.js │ ├── url-match-pattern-list.test.js │ ├── url-match-pattern.test.js │ ├── url.test.js │ ├── util.test.js │ ├── variable-list.test.js │ ├── variable-scope.test.js │ ├── variable.test.js │ └── version.test.js └── types └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | max_length = 120 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.{json, yml, html, hbs}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior 2 | * text=auto 3 | 4 | # JavaScript files will always have LF line endings 5 | *.js text eol=lf 6 | 7 | # JSON files always have LF line endings 8 | *.json text eol=lf 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | day: "saturday" 8 | time: "03:14" 9 | timezone: Asia/Calcutta 10 | rebase-strategy: "disabled" 11 | open-pull-requests-limit: 10 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | - '*.md' 8 | pull_request: 9 | branches: [$default-branch] 10 | schedule: 11 | - cron: '0 12 * * 0' 12 | 13 | jobs: 14 | lint: 15 | name: Lint 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Use Node.js 18.x 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 18.x 26 | cache: 'npm' 27 | 28 | - name: Install 29 | run: npm ci 30 | 31 | - name: Run lint tests 32 | run: npm run test-lint 33 | 34 | browser-tests: 35 | name: Browser Tests 36 | runs-on: ubuntu-latest 37 | 38 | steps: 39 | - name: Checkout repository 40 | uses: actions/checkout@v3 41 | 42 | - name: Use Node.js 18.x 43 | uses: actions/setup-node@v3 44 | with: 45 | node-version: 18.x 46 | cache: 'npm' 47 | 48 | - name: Install 49 | run: npm ci 50 | 51 | - name: Run browser tests 52 | run: npm run test-browser 53 | 54 | tests: 55 | name: Tests 56 | runs-on: ${{ matrix.os }} 57 | strategy: 58 | fail-fast: false 59 | matrix: 60 | node-version: [18, 20] 61 | os: [ubuntu-latest, windows-latest] 62 | include: 63 | - coverage: true 64 | node-version: latest 65 | os: ubuntu-latest 66 | 67 | steps: 68 | - name: Checkout repository 69 | uses: actions/checkout@v3 70 | 71 | - name: Use Node.js ${{ matrix.node-version }} 72 | uses: actions/setup-node@v3 73 | with: 74 | node-version: ${{ matrix.node-version }} 75 | cache: 'npm' 76 | 77 | - name: Install 78 | run: npm ci 79 | 80 | - name: Run system tests 81 | run: npm run test-system 82 | 83 | - name: Run unit tests 84 | run: npm run test-unit 85 | 86 | - if: ${{ matrix.coverage }} 87 | name: Upload coverage 88 | run: npm run codecov -- -c -Z -f .coverage/coverage-final.json -F unit -t ${{ secrets.CODECOV_TOKEN }} 89 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [develop, gh-pages, main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [develop] 9 | schedule: 10 | - cron: '0 12 * * 0' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | # Override automatic language detection by changing the below list 21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 22 | language: ['javascript'] 23 | # Learn more... 24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | with: 30 | # We must fetch at least the immediate parents so that if this is 31 | # a pull request then we can checkout the head. 32 | fetch-depth: 2 33 | 34 | # If this run was triggered by a pull request event, then checkout 35 | # the head of the pull request instead of the merge commit. 36 | - run: git checkout HEAD^2 37 | if: ${{ github.event_name == 'pull_request' }} 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | # If you wish to specify custom queries, you can do so here or in a config file. 45 | # By default, queries listed here will override any specified in a config file. 46 | # Prefix the list here with "+" to use these queries and those in the config file. 47 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 48 | 49 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 50 | # If this step fails, then you should remove it and run the build manually (see below) 51 | - name: Autobuild 52 | uses: github/codeql-action/autobuild@v1 53 | 54 | # ℹ️ Command-line programs to run using the OS shell. 55 | # 📚 https://git.io/JvXDl 56 | 57 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 58 | # and modify them (or add more) to build your code if your project 59 | # uses a compiled language 60 | 61 | #- run: | 62 | # make bootstrap 63 | # make release 64 | 65 | - name: Perform CodeQL Analysis 66 | uses: github/codeql-action/analyze@v1 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PLATFORM 2 | # ======== 3 | # All exclusions that are specific to the NPM, GIT, IDE and Operating Systems. 4 | 5 | # - Do not allow installed node modules to be committed. Doing `npm install -d` will bring them in root or other places. 6 | node_modules 7 | 8 | # - Do not commit any log file from anywhere 9 | *.log 10 | *.log.* 11 | 12 | # - Prevent addition of OS specific file explorer files 13 | Thumbs.db 14 | .DS_Store 15 | 16 | # Prevent IDE stuff 17 | .idea 18 | .vscode 19 | 20 | # PROJECT 21 | # ======= 22 | # Configuration pertaining to project specific repository structure. 23 | 24 | # - Prevent Sublime text IDE files from being committed to repository 25 | *.sublime-* 26 | 27 | # - Allow sublime text project file to be committed in the development directory. 28 | !/develop/*.sublime-project 29 | 30 | # - Prevent CI output files from being Added 31 | /out/ 32 | 33 | # - Prevent diff backups from SourceTree from showing as commit. 34 | *.BACKUP.* 35 | *.BASE.* 36 | *.LOCAL.* 37 | *.REMOTE.* 38 | *.orig 39 | 40 | # - Prevent unit test coverage reports from being committed to the repository 41 | .coverage 42 | .nyc_output 43 | -------------------------------------------------------------------------------- /.jsdoc-config-type-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": [ 5 | "jsdoc", 6 | "closure" 7 | ] 8 | }, 9 | "source": { 10 | "include": [ 11 | "lib" 12 | ], 13 | "includePattern": ".+\\.js(doc)?$", 14 | "excludePattern": "(^|\\/|\\\\)_" 15 | }, 16 | "plugins": [ 17 | "tsd-jsdoc/dist/plugin" 18 | ], 19 | "templates": { 20 | "cleverLinks": true, 21 | "default": { 22 | "outputSourceFiles": false 23 | } 24 | }, 25 | "opts": { 26 | "destination": "./types", 27 | "template": "tsd-jsdoc/dist", 28 | "outFile": "index.d.ts", 29 | "recurse": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.jsdoc-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc", "closure"] 5 | }, 6 | "source": { 7 | "include": [ ], 8 | "includePattern": ".+\\.js(doc)?$", 9 | "excludePattern": "(^|\\/|\\\\)_" 10 | }, 11 | 12 | "plugins": [ 13 | "plugins/markdown" 14 | ], 15 | 16 | "templates": { 17 | "cleverLinks": false, 18 | "monospaceLinks": false, 19 | "highlightTutorialCode" : true 20 | }, 21 | 22 | "opts": { 23 | "template": "./node_modules/postman-jsdoc-theme", 24 | "encoding": "utf8", 25 | "destination": "./out/docs", 26 | "recurse": true, 27 | "readme": "README.md" 28 | }, 29 | 30 | "markdown": { 31 | "parser": "gfm", 32 | "hardwrap": false 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # PLATFORM 2 | # ======== 3 | # All exclusions that are specific to the NPM, GIT, IDE and Operating Systems. 4 | 5 | # - Do not allow installed node modules to be committed. Doing `npm install -d` will bring them in root or other places. 6 | node_modules 7 | 8 | # - Do not commit any log file from anywhere 9 | *.log 10 | *.log.* 11 | 12 | # - Prevent addition of OS specific file explorer files 13 | Thumbs.db 14 | .DS_Store 15 | 16 | # Prevent IDE stuff 17 | .idea 18 | .vscode 19 | 20 | # PROJECT 21 | # ======= 22 | # Configuration pertaining to project specific repository structure. 23 | 24 | # - Prevent Sublime text IDE files from being committed to repository 25 | *.sublime-* 26 | 27 | # - Allow sublime text project file to be committed in the development directory. 28 | !/develop/*.sublime-project 29 | 30 | # - Prevent CI output files from being added 31 | /out/ 32 | 33 | # - Prevent diff backups from SourceTree from showing as commit. 34 | *.BACKUP.* 35 | *.BASE.* 36 | *.LOCAL.* 37 | *.REMOTE.* 38 | *.orig 39 | 40 | # - Prevent code coverage reports from being added 41 | .coverage 42 | .nyc_output 43 | 44 | # - Prevent config and test files from being added 45 | .git* 46 | npm/ 47 | test/ 48 | docs/ 49 | examples/ 50 | benchmark/ 51 | .eslintrc 52 | .nycrc.js 53 | codecov.yml 54 | .editorconfig 55 | .jsdoc-config.json 56 | .jsdoc-config-type-def.json 57 | -------------------------------------------------------------------------------- /.nycrc.js: -------------------------------------------------------------------------------- 1 | const TEST_TYPE = ((argv) => { 2 | let match = argv[argv.length - 1].match(/npm\/test-(\w+).js/); 3 | 4 | return match && match[1] || ''; 5 | })(process.argv); 6 | 7 | function configOverrides(testType) { 8 | switch (testType) { 9 | case 'unit': 10 | return { 11 | statements: 100, 12 | branches: 100, 13 | functions: 100, 14 | lines: 100 15 | }; 16 | default: 17 | return {} 18 | } 19 | } 20 | 21 | module.exports = { 22 | all: true, 23 | 'check-coverage': true, 24 | 'report-dir': '.coverage', 25 | 'temp-dir': '.nyc_output', 26 | include: ['lib/**/*.js'], 27 | reporter: ['lcov', 'json', 'text', 'text-summary'], 28 | ...configOverrides(TEST_TYPE), 29 | }; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Postman Collection SDK [![Build Status](https://github.com/postmanlabs/postman-collection/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/postmanlabs/postman-collection/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/postmanlabs/postman-collection/branch/develop/graph/badge.svg)](https://codecov.io/gh/postmanlabs/postman-collection) 2 | 3 | Postman Collection SDK is a NodeJS module that allows a developer to work with Postman Collections. Using this module a 4 | developer can create collections, manipulate them and then export them in a format that the Postman Apps and Postman CLI 5 | Runtimes (such as [Newman](https://github.com/postmanlabs/newman)) can consume. 6 | 7 | A collection lets you group individual requests together. These requests can be further organized into folders to 8 | accurately mirror your API. Requests can also store sample responses when saved in a collection. You can add metadata 9 | like name and description too so that all the information that a developer needs to use your API is available easily. 10 | 11 | To know more about Postman Collections, visit the 12 | [collection documentation section on Postman Website](https://postman.com/collection/). 13 | 14 | > The new [Collection Format v2](https://blog.postman.com/travelogue-of-postman-collection-format-v2/) 15 | > builds a stronger foundation for improving your productivity while working with APIs. We want your feedback and iron 16 | > out issues before this goes into the Postman Apps. 17 | 18 | ## Installing the SDK 19 | 20 | Postman Collection SDK can be installed using NPM or directly from the git repository within your NodeJS projects. If 21 | installing from NPM, the following command installs the SDK and saves in your `package.json` 22 | 23 | ```terminal 24 | > npm install postman-collection --save 25 | ``` 26 | 27 | 28 | ## Getting Started 29 | 30 | In this example snippet we will get started by loading a collection from a file and output the same in console. 31 | 32 | ```javascript 33 | var fs = require('fs'), // needed to read JSON file from disk 34 | Collection = require('postman-collection').Collection, 35 | myCollection; 36 | 37 | // Load a collection to memory from a JSON file on disk (say, sample-collection.json) 38 | myCollection = new Collection(JSON.parse(fs.readFileSync('sample-collection.json').toString())); 39 | 40 | // log items at root level of the collection 41 | console.log(myCollection.toJSON()); 42 | ``` 43 | 44 | After loading the collection from file, one can do a lot more using the functions that are available in the SDK. To know 45 | more about these functions, head over to 46 | [Collection SDK Docs](http://www.postmanlabs.com/postman-collection). 47 | 48 | ## Postman Collection Schema 49 | 50 | The collection schema outlines the JSON definition of data structure accepted by the constructor of each properties of 51 | this SDK. In other words, this SDK provides JavaScript level object manipulation for the JSON structure defined by 52 | Postman Collection Format in [http://schema.postman.com/](http://schema.postman.com/). 53 | 54 | | Schema Version | Compatible SDK Versions | 55 | |----------------|-------------------------| 56 | | 1.0 | none | 57 | | 2.0 | <3.0 | 58 | | 2.1 | >= 3.0 | 59 | 60 | Conceptually, a JSON input to the constructor of an SDK property should provide similar output when that property 61 | instance's `.toJSON()` is called. 62 | -------------------------------------------------------------------------------- /benchmark/benchmark-results.json: -------------------------------------------------------------------------------- 1 | { 2 | "suites": [ 3 | { 4 | "name": "SuperString variable substitution", 5 | "scenarios": [ 6 | { 7 | "name": "single constant dynamic variable", 8 | "executions": 411989, 9 | "time": 12136.21698637585, 10 | "error": 2.9153562603877248 11 | }, 12 | { 13 | "name": "two constant dynamic variables", 14 | "executions": 223254, 15 | "time": 22395.94219588451, 16 | "error": 0.5184261312204904 17 | }, 18 | { 19 | "name": "four constant dynamic variables", 20 | "executions": 96120, 21 | "time": 52018.067613399915, 22 | "error": 0.7425831865647227 23 | }, 24 | { 25 | "name": "eight constant dynamic variables", 26 | "executions": 73825, 27 | "time": 67727.10917710802, 28 | "error": 0.6059831169932062 29 | }, 30 | { 31 | "name": "sixteen constant dynamic variables", 32 | "executions": 59716, 33 | "time": 83729.05474244758, 34 | "error": 0.8958354666864753 35 | }, 36 | { 37 | "name": "thirty-two constant dynamic variables", 38 | "executions": 19165, 39 | "time": 260888.65687451084, 40 | "error": 0.6319144162829673 41 | }, 42 | { 43 | "name": "sixty-four constant dynamic variables", 44 | "executions": 8866, 45 | "time": 563898.6565531243, 46 | "error": 68.6127576990454 47 | } 48 | ] 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 70..100 # green if 100+, red if 70- 3 | 4 | status: 5 | patch: 6 | # coverage status for pull request diff 7 | default: 8 | target: 100 # any patch should be 100% covered 9 | threshold: 1% # allow a little drop 10 | 11 | project: 12 | # coverage status for whole project 13 | default: 14 | target: auto # use coverage of base commit as target 15 | threshold: 1% # allow a little drop 16 | 17 | # coverage status for unit tests 18 | unit: 19 | target: 100 20 | flags: 21 | - unit 22 | 23 | parsers: 24 | javascript: 25 | enable_partials: yes # use partial line coverage 26 | -------------------------------------------------------------------------------- /docs/concepts.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | This is a gentle introduction to the sort of functionality that the SDK provides. 4 | 5 | ## Collection Hierarchy 6 | At a very high level, Collections are organized in the following way: 7 | ``` 8 | 9 | +------------+ 10 | +--------------------------+ Collection +------------------------+ 11 | | +------+-----+ | 12 | | | | 13 | | | | 14 | +-------v---------+ +--v---+ +-----v-----+ 15 | |ItemGroup(Folder)| +----------+ Item +------------+ |Information| 16 | +-+-----------+---+ | +---+--+ | +-----------+ 17 | | | | | | 18 | +--------v--+ +---v--+ +----v----+ +----v------+ +---v----+ 19 | | ItemGroup | | Item | | Request | | Responses | | Events | 20 | +-----------+ +------+ +---------+ +-----------+ +--------+ 21 | ``` 22 | 23 | ### Collection 24 | 25 | A collection can contain a number of `Item`s, `ItemGroup`s and can have a single 26 | information block. 27 | 28 | E.g: **A very simple Collection** 29 | ```json 30 | { 31 | "information": { 32 | "name": "My Collection", 33 | "version": "v2.0.0", 34 | "description": "This is a demo collection.", 35 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/" 36 | }, 37 | "item": [] 38 | } 39 | ``` 40 | 41 | The "schema" property is required, and must be set to the proper URL as above. 42 | The "item" array contains Folders (ItemGroups) or Items. 43 | 44 | ### Item 45 | An Item is the basic building block for a collection. It represents an HTTP request, 46 | along with the associated metadata. 47 | 48 | E.g: **A simple Item** 49 | ```json 50 | { 51 | "id": "my-first-item", 52 | "name": "My First Item", 53 | "description": "This is an Item that contains a single HTTP GET request. It doesn't really do much yet!", 54 | "request": "http://echo.getpostman.com/get", 55 | "response": [] 56 | } 57 | ``` 58 | 59 | An Item can have multiple saved responses, which are stored in the `response` array. Response is further elaborated below. 60 | While we've defined a very simple request above, it can become really complicated, as you'll soon see! 61 | 62 | ### ItemGroup (Folder) 63 | An ItemGroup is a simple container for Items. Literally, a Collection is just an ItemGroup with a special 64 | `information` block. 65 | 66 | E.g: **An ItemGroup with two Items** 67 | ```json 68 | { 69 | "id": "my-first-itemgroup", 70 | "name": "First Folder", 71 | "description": "This ItemGroup (Folder) contains two Items.", 72 | "item": [ 73 | { 74 | "id": "1", 75 | "name": "Item A", 76 | "request": "http://echo.getpostman.com/get" 77 | }, 78 | { 79 | "id": "2", 80 | "name": "Item B", 81 | "request": "http://echo.getpostman.com/headers" 82 | } 83 | ] 84 | } 85 | ``` 86 | The `item` array above contains the Items in this ItemGroup. 87 | 88 | ### Request 89 | A Request represents an HTTP request. Usually, a Request belongs to an Item. Requests can be specified as a string 90 | (check the example above) or as a JSON Object. If specified as a string, it is assumed to be a GET request. 91 | 92 | E.g: **A mildly complicated Request** 93 | ```json 94 | { 95 | "description": "This is a sample POST request", 96 | "url": "https://echo.getpostman.com/post", 97 | "method": "POST", 98 | "header": [ 99 | { 100 | "key": "Content-Type", 101 | "value": "application/json" 102 | }, 103 | { 104 | "key": "Host", 105 | "value": "echo.getpostman.com" 106 | } 107 | ], 108 | "body": { 109 | "mode": "urlencoded", 110 | "urlencoded": [ 111 | { 112 | "key": "my-body-variable", 113 | "value": "Something Awesome!" 114 | } 115 | ] 116 | } 117 | } 118 | ``` 119 | For more on request bodies, check the API documentation: {@link RequestBody} 120 | 121 | E.g: **A simple request with Authentication information** 122 | ```json 123 | { 124 | "url": "https://echo.getpostman.com/basic-auth", 125 | "method": "GET", 126 | "auth": { 127 | "type": "basic", 128 | "basic": { 129 | "username": "fred", 130 | "password": "hunter2" 131 | } 132 | } 133 | } 134 | ``` 135 | The SDK supports a number of auth types, refer to the API documentation: {@link RequestAuth} 136 | 137 | ### Events 138 | 139 | The SDK currently supports two events: 140 | 141 | 1. Test Script: `test` 142 | You can associate a test script with an Item. The test script is usually executed _after_ the actual HTTP request is 143 | sent, and the response is received. 144 | 145 | 2. Pre-Request Script: `prerequest` 146 | Pre-Request scripts are usually executed _before_ the HTTP request is sent. 147 | 148 | E.g: **An Item with Events** 149 | ```json 150 | { 151 | "id": "evented-item", 152 | "name": "Item with Events", 153 | "description": "This is an Item that contains `test and `prerequest` events.", 154 | "request": "http://echo.getpostman.com/get", 155 | "event": [ 156 | { 157 | "listen": "prerequest", 158 | "script": { 159 | "type": "text/javascript", 160 | "exec": "console.log('We are in the pre-request script, the request has not run yet!')" 161 | } 162 | }, 163 | { 164 | "listen": "test", 165 | "script": { 166 | "type": "text/javascript", 167 | "exec": "console.log('We are using the test script now, and the request was already sent!')" 168 | } 169 | } 170 | ] 171 | } 172 | ``` 173 | -------------------------------------------------------------------------------- /docs/mutation-tracking.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | 3 | A mutation represents a change to the state of an object. To track a mutation would mean to capture the transition from 4 | one state to another. This means capturing the state transition as a serializeable object that can be transported and 5 | reapplied on an different object. Once captured the same mutation when applied on two separate objects with same initial 6 | state should transition them to the same final state. 7 | 8 | ### When would you need need to track mutations? 9 | 10 | Let's take executing collection scripts in `postman-sandbox` for example. Let's say you pass down an environment and a 11 | script that updates your environment, to the sandbox to execute. Once the script finishes executing you would get the 12 | final state of the environment after the updates. Now you have the final state of the environment. But you do not know 13 | what updates the script did to your environment. Also you do not know all the intermediate states of the environment 14 | when the script was executing. 15 | 16 | Now imagine you could track every change to the environment and get it at the end of script execution. You can replay 17 | them again from an initial state, pause at any point and continue. Mutation tracking can help you with that. 18 | 19 | You would typically want to track mutations when you want to track individual changes to an object and store/transport 20 | them. You might also need mutation tracking when you want to observe changes on an object and replay them on a different 21 | object to achieve mirroring. 22 | 23 | ### Basic Components 24 | 25 | * **Mutation**: The basic component of mutation tracking system is an individual mutation. This captures a single change. 26 | A mutation is serializeable and can be applied to any object of the similar type. 27 | * **MutationTracker**: Mutation tracker collects a sequence of mutations and provides helpers to work with mutations, like 28 | applying them on a new object. The mutation tracker can also optimize the sequence of mutations, like compressing them. 29 | 30 | ## Representing a mutation 31 | 32 | Every mutation has four parts 33 | 34 | * **Target** - The origin of the mutation 35 | * **Key Path** - Path to the property of the target that got mutated 36 | * **Instruction** - The nature of mutation 37 | * **Value (Optional)** - The payload for the instruction 38 | 39 | To capture all the information in an optimized format we use a serialization spec based on - JSON Delta (http://json-delta.readthedocs.io/en/latest/) 40 | 41 | An example `set` operation would look like - `['foo', 1]` 42 | 43 | If we break this down 44 | 45 | ``` 46 | sample.mutations = [['foo', 1]] 47 | | |___|_______ Key Path 48 | | |_______ Value 49 | |_________________________________ Target 50 | ``` 51 | 52 | You would have noticed that the instruction is not explicitly captured. That's because it is derived from the parameter set. 53 | 54 | For example an unset operation would look like `['foo']`. 55 | 56 | Which means you can derive the instruction based on the shape of the mutation itself. A mutation with single parameter 57 | would imply an unset operation. A mutation with two parameters would imply a set operation. 58 | 59 | ### Mutation tracking in Variable Scopes 60 | 61 | To track mutations in a Postman Variable Scope, you can enabled it like 62 | 63 | ```js 64 | var VariableScope = require('postman-collection').VariableScope, 65 | environment = new VariableScope(); 66 | 67 | // enables tracking mutations 68 | environment.enableTracking(); 69 | ``` 70 | 71 | From this point onwards any `set/unset/clear` operations on `environment` will be captured in in `environment.mutations`. 72 | 73 | You can then apply the same mutation on a different object like 74 | 75 | ```js 76 | var environmentCopy = new VariableScope(); 77 | 78 | // applies the mutations captured on `environment` into `environmentCopy` making it a mirror 79 | environment.mutations.applyOn(environmentCopy); 80 | ``` 81 | 82 | ### Limitations and scope for extension 83 | 84 | Postman collection SDK supports tracking mutations on `VariableScopes`. We could apply the same concepts to any type 85 | of object not restricted to a `VariableScope`. 86 | 87 | In practise more complex types would have complex instructions that are not restricted to `set/unset`. To capture those 88 | we might have to extend our representation for a mutation to support more complex instructions. 89 | 90 | We will have to capture the instruction explicitly in that case. To do so we can think of something like 91 | 92 | ``` 93 | ['id', 'addRequest', '#', '1', 'request1'] 94 | |________|_________|____|________|___________ Mutation Id 95 | |_________|____|________|___________ Instruction 96 | |____|________|___________ Delimiter to differentiate from first generation mutations 97 | |________|___________ Key Path 98 | |___________ Value 99 | ``` -------------------------------------------------------------------------------- /docs/tutorial-jsdoc-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "concepts": { 3 | "title": "Postman SDK Concepts" 4 | }, 5 | "mutation-tracking": { 6 | "title": "Tracking Variable Mutations" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/console-readout.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | stripJSONComments = require('strip-json-comments'), 3 | Collection = require('../lib/index').Collection; 4 | 5 | fs.readFile(process.argv.slice(2).pop(), 'utf8', function (err, data) { 6 | if (err) { 7 | throw err; 8 | } 9 | 10 | try { 11 | data = new Collection(JSON.parse(stripJSONComments(data))); 12 | } 13 | catch (e) { 14 | throw e; 15 | } 16 | 17 | console.dir(data, { colors: true, depth: 10000 }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/digest.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "name": "Echo - Digest", 5 | "_postman_id": "10084d91-feb7-91f8-cea3-8432b25502e4", 6 | "description": "Postman Echo is service you can use to test your REST clients and make sample API calls. It provides endpoints for `GET`, `POST`, `PUT`, various auth mechanisms and other utility endpoints.", 7 | "schema": "http://schema.getpostman.com/collection/v2/collection.json" 8 | }, 9 | "item": [ 10 | { 11 | "name": "Auth: Digest", 12 | "description": "Digest authentication protects an endpoint with a username and password without actually transmitting the password over network.\nOne has to apply a hash function (like MD5, etc) to the username and password before sending them over the network.\n\n> Username: `postman`\n>\n> Password: `password`\n\nUnlike Basic-Auth, authentication happens using two consecutive requests where the first request returns `401 Unauthorised` along with `WWW-Authenticate` header containing information that needs to be used to authenticate subsequent calls.\n\nTo know more about digest authentication, refer to the [Digest Access Authentication](https://en.wikipedia.org/wiki/Digest_access_authentication) wikipedia article.\nThe article on [authentication helpers](https://www.getpostman.com/docs/helpers#digest-auth) elaborates how to use the same within the Postman app.", 13 | "item": [ 14 | { 15 | "id": "9808ab99-bb91-6d83-13e4-0219c2377e35", 16 | "name": "DigestAuth Request", 17 | "event": [ 18 | { 19 | "listen": "test", 20 | "script": { 21 | "type": "text/javascript", 22 | "exec": "tests[\"response code is 401\"] = responseCode.code === 401;\ntests[\"response has WWW-Authenticate header\"] = (postman.getResponseHeader('WWW-Authenticate'));\n\nvar authenticateHeader = postman.getResponseHeader('WWW-Authenticate'),\n realmStart = authenticateHeader.indexOf('\"',authenticateHeader.indexOf(\"realm\")) + 1 ,\n realmEnd = authenticateHeader.indexOf('\"',realmStart),\n realm = authenticateHeader.slice(realmStart,realmEnd),\n nonceStart = authenticateHeader.indexOf('\"',authenticateHeader.indexOf(\"nonce\")) + 1,\n nonceEnd = authenticateHeader.indexOf('\"',nonceStart),\n nonce = authenticateHeader.slice(nonceStart,nonceEnd);\n \npostman.setGlobalVariable('echo_digest_realm', realm);\npostman.setGlobalVariable('echo_digest_nonce', nonce);" 23 | } 24 | } 25 | ], 26 | "request": { 27 | "auth": { 28 | "type": "noauth", 29 | "noauth": {} 30 | }, 31 | "url": "https://echo.getpostman.com/digest-auth", 32 | "method": "GET", 33 | "header": "Content-Type: application/json\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1448549987\", nonce=\"eOJZCd\", mac=\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\"\n", 34 | "body": { 35 | "mode": "formdata", 36 | "formdata": [ 37 | { 38 | "key": "code", 39 | "value": "xWnkliVQJURqB2x1", 40 | "type": "text", 41 | "enabled": true 42 | }, 43 | { 44 | "key": "grant_type", 45 | "value": "authorization_code", 46 | "type": "text", 47 | "enabled": true 48 | }, 49 | { 50 | "key": "redirect_uri", 51 | "value": "https://www.getpostman.com/oauth2/callback", 52 | "type": "text", 53 | "enabled": true 54 | }, 55 | { 56 | "key": "client_id", 57 | "value": "abc123", 58 | "type": "text", 59 | "enabled": true 60 | }, 61 | { 62 | "key": "client_secret", 63 | "value": "ssh-secret", 64 | "type": "text", 65 | "enabled": true 66 | } 67 | ] 68 | }, 69 | "description": "Performing a simple `GET` request to this endpoint returns status code `401 Unauthorized` with `WWW-Authenticate` header containing information to successfully authenticate subsequent requests.\nThe `WWW-Authenticate` header must be processed to extract `realm` and `nonce` values to hash subsequent requests.\n\nWhen this request is executed within Postman, the script attached with this request does the hard work of extracting realm and nonce from the header and set it as [global variables](https://www.getpostman.com/docs/environments#global-variables) named `echo_digest_nonce` and `echo_digest_realm`.\nThese variables are re-used in subsequent request for seamless integration of the two requests." 70 | }, 71 | "response": [ 72 | { 73 | "name": "a sample response", 74 | "originalRequest": "http://echo.getpostman.com/status/200", 75 | "status": "200 OK", 76 | "code": 200, 77 | "header": "Content-Type: application/json\nAuthorization: Hawk id=\"dh37fgj492je\", ts=\"1448549987\", nonce=\"eOJZCd\", mac=\"O2TFlvAlMvKVSKOzc6XkfU6+5285k5p3m5dAjxumo2k=\"\n", 78 | "cookie": [ 79 | { 80 | "domain": ".httpbin.org", 81 | "expires": 1502442248, 82 | "hostOnly": false, 83 | "httpOnly": false, 84 | "name": "_ga", 85 | "path": "/", 86 | "secure": false, 87 | "session": false, 88 | "_postman_storeId": "0", 89 | "value": "GA1.2.113558537.1435817423" 90 | } 91 | ], 92 | "body": "response body" 93 | } 94 | ] 95 | }, 96 | { 97 | "id": "ee34b510-6e68-1280-1160-bc57bc29436e", 98 | "name": "DigestAuth Success", 99 | "event": [ 100 | { 101 | "listen": "test", 102 | "script": { 103 | "type": "text/javascript", 104 | "exec": "tests[\"response code is 200\"] = responseCode.code === 200;\ntests[\"body contains authenticated\"] = responseBody.has(\"authenticated\");" 105 | } 106 | } 107 | ], 108 | "request": { 109 | "auth": { 110 | "type": "digest", 111 | "digest": { 112 | "algorithm": "MD5", 113 | "username": "postman", 114 | "realm": "{{echo_digest_realm}}", 115 | "password": "password", 116 | "nonce": "{{echo_digest_nonce}}", 117 | "nonceCount": "", 118 | "clientNonce": "", 119 | "opaque": "", 120 | "qop": "" 121 | } 122 | }, 123 | "url": "https://echo.getpostman.com/digest-auth", 124 | "method": "GET", 125 | "header": [ 126 | { 127 | "key": "Authorization", 128 | "value": "Digest username=\"postman\", realm=\"Users\", nonce=\"ZKMCeJ68NEK1mpeg0i6RMXX7U8qANc9g\", uri=\"/digest-auth\", response=\"5cf1155abf5172ea751daec57db07340\", opaque=\"\"", 129 | "description": "" 130 | } 131 | ], 132 | "body": { 133 | "mode": "formdata", 134 | "formdata": [] 135 | }, 136 | "description": "This endpoint sends a hashed Digest Authorization header to gain access to a valid `200 Ok` response code. In Postman, it uses the stored [global variables](https://www.getpostman.com/docs/environments#gloval-variables), `echo_digest_realm` and `echo_digest_nonce`, to generate the hashed authorisation header.\n\nWithin Postman, for this request to successfully authenticate, running the previous request \"DigestAuth Request\" stores the relevant information within the global variables." 137 | } 138 | } 139 | ] 140 | } 141 | ] 142 | } 143 | -------------------------------------------------------------------------------- /examples/hawk.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "name": "hawkAuthTest", 5 | "_postman_id": "d497d10e-e280-8c83-709a-a4d4ea12ad14", 6 | "description": "", 7 | "schema": "http://schema.getpostman.com/collection/v2/collection.json" 8 | }, 9 | "item": [ 10 | { 11 | "id": "951fc3e8-c6b6-5c19-9f69-4e7499b3127f", 12 | "name": "test hawk auth success", 13 | "request": { 14 | "auth": { 15 | "type": "hawk", 16 | "hawk": { 17 | "authId": "dh37fgj492je", 18 | "authKey": "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn", 19 | "algorithm": "sha256", 20 | "user": "asda", 21 | "saveHelperData": true, 22 | "nonce": "eFRP2o", 23 | "extraData": "skjdfklsjhdflkjhsdf", 24 | "appId": "", 25 | "delegation": "", 26 | "timestamp": "" 27 | } 28 | }, 29 | "url": "http://echo.getpostman.com/auth/hawk", 30 | "method": "GET", 31 | "header": [ 32 | { 33 | "key": "Authorization", 34 | "value": "Hawk id=\"dh37fgj492je\", ts=\"1448888081\", nonce=\"HoH6Ay\", ext=\"skjdfklsjhdflkjhsdf\", mac=\"moWleO5f/8QbvIiy7oo2zj1bmezhrYwrCkz4BsXg0M4=\"", 35 | "description": "" 36 | } 37 | ], 38 | "body": { 39 | "mode": "formdata", 40 | "formdata": [] 41 | }, 42 | "description": "" 43 | }, 44 | "response": [] 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /examples/nested-v2-collection-without-name.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "_postman_id": "e5f2e9cf-173b-c60a-7336-ac804a87d762", 5 | "description": "A simple V2 collection to test out multi level folder flows", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "id": "F1-id", 11 | "description": "", 12 | "item": [ 13 | { 14 | "event": [ 15 | { 16 | "listen": "test", 17 | "script": { 18 | "type": "text/javascript", 19 | "exec": [ 20 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 21 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === 0;" 22 | ] 23 | } 24 | } 25 | ], 26 | "request": { 27 | "url": "https://postman-echo.com/get", 28 | "method": "GET", 29 | "header": [], 30 | "body": {}, 31 | "description": "" 32 | }, 33 | "response": [], 34 | "id": "F1.R1-id" 35 | }, 36 | { 37 | "event": [ 38 | { 39 | "listen": "test", 40 | "script": { 41 | "type": "text/javascript", 42 | "exec": [ 43 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 44 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"1\";" 45 | ] 46 | } 47 | } 48 | ], 49 | "request": { 50 | "url": "https://postman-echo.com/get", 51 | "method": "GET", 52 | "header": [], 53 | "body": {}, 54 | "description": "" 55 | }, 56 | "response": [], 57 | "id": "F1.R2-id" 58 | }, 59 | { 60 | "event": [ 61 | { 62 | "listen": "test", 63 | "script": { 64 | "type": "text/javascript", 65 | "exec": [ 66 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 67 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"2\";" 68 | ] 69 | } 70 | } 71 | ], 72 | "request": { 73 | "url": "https://postman-echo.com/get", 74 | "method": "GET", 75 | "header": [], 76 | "body": {}, 77 | "description": "" 78 | }, 79 | "response": [], 80 | "id": "F1.R3-id" 81 | } 82 | ] 83 | }, 84 | { 85 | "id": "F2-id", 86 | "description": "", 87 | "item": [ 88 | { 89 | "id": "F2.F3-id", 90 | "description": "", 91 | "item": [ 92 | { 93 | "id": "F2.F3.R1-id", 94 | "event": [ 95 | { 96 | "listen": "prerequest", 97 | "script": { 98 | "type": "text/javascript", 99 | "exec": [ 100 | "var count = parseInt(postman.getEnvironmentVariable(\"count\"));", 101 | "postman.setEnvironmentVariable(\"count\", isNaN(count) ? 0 : count + 1);" 102 | ] 103 | } 104 | }, 105 | { 106 | "listen": "test", 107 | "script": { 108 | "type": "text/javascript", 109 | "exec": [ 110 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 111 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"3\";" 112 | ] 113 | } 114 | } 115 | ], 116 | "request": { 117 | "url": "https://postman-echo.com/get", 118 | "method": "GET", 119 | "header": [], 120 | "body": {}, 121 | "description": "" 122 | }, 123 | "response": [] 124 | } 125 | ] 126 | }, 127 | { 128 | "description": "", 129 | "item": [] 130 | }, 131 | { 132 | "event": [ 133 | { 134 | "listen": "test", 135 | "script": { 136 | "type": "text/javascript", 137 | "exec": [ 138 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 139 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"4\";" 140 | ] 141 | } 142 | } 143 | ], 144 | "request": { 145 | "url": "https://postman-echo.com/get", 146 | "method": "GET", 147 | "header": [], 148 | "body": {}, 149 | "description": "" 150 | }, 151 | "response": [] 152 | } 153 | ] 154 | }, 155 | { 156 | "event": [ 157 | { 158 | "listen": "prerequest", 159 | "script": { 160 | "type": "text/javascript", 161 | "exec": [ 162 | "var count = parseInt(postman.getEnvironmentVariable(\"count\"));", 163 | "postman.setEnvironmentVariable(\"count\", isNaN(count) ? 0 : count + 1);" 164 | ] 165 | } 166 | }, 167 | { 168 | "listen": "test", 169 | "script": { 170 | "type": "text/javascript", 171 | "exec": [ 172 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 173 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"5\";" 174 | ] 175 | } 176 | } 177 | ], 178 | "request": { 179 | "url": "https://postman-echo.com/get", 180 | "method": "GET", 181 | "header": [], 182 | "body": {}, 183 | "description": "" 184 | }, 185 | "response": [], 186 | "id": "R1-id" 187 | } 188 | ] 189 | } 190 | -------------------------------------------------------------------------------- /examples/nested-v2-collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "name": "multi-level-folders-v2", 5 | "_postman_id": "e5f2e9cf-173b-c60a-7336-ac804a87d762", 6 | "description": "A simple V2 collection to test out multi level folder flows", 7 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" 8 | }, 9 | "item": [ 10 | { 11 | "name": "F1", 12 | "id": "F1-id", 13 | "description": "", 14 | "item": [ 15 | { 16 | "name": "F1.R1", 17 | "event": [ 18 | { 19 | "listen": "test", 20 | "script": { 21 | "type": "text/javascript", 22 | "exec": [ 23 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 24 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === 0;" 25 | ] 26 | } 27 | } 28 | ], 29 | "request": { 30 | "url": "https://postman-echo.com/get", 31 | "method": "GET", 32 | "header": [], 33 | "body": {}, 34 | "description": "" 35 | }, 36 | "response": [], 37 | "id": "F1.R1-id" 38 | }, 39 | { 40 | "name": "F1.R2", 41 | "event": [ 42 | 43 | { 44 | "listen": "test", 45 | "script": { 46 | "type": "text/javascript", 47 | "exec": [ 48 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 49 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"1\";" 50 | ] 51 | } 52 | } 53 | ], 54 | "request": { 55 | "url": "https://postman-echo.com/get", 56 | "method": "GET", 57 | "header": [], 58 | "body": {}, 59 | "description": "" 60 | }, 61 | "response": [], 62 | "id": "F1.R2-id" 63 | }, 64 | { 65 | "name": "F1.R3", 66 | "event": [ 67 | { 68 | "listen": "test", 69 | "script": { 70 | "type": "text/javascript", 71 | "exec": [ 72 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 73 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"2\";" 74 | ] 75 | } 76 | } 77 | ], 78 | "request": { 79 | "url": "https://postman-echo.com/get", 80 | "method": "GET", 81 | "header": [], 82 | "body": {}, 83 | "description": "" 84 | }, 85 | "response": [] 86 | } 87 | ] 88 | }, 89 | { 90 | "name": "F2", 91 | "id": "F2-id", 92 | "description": "", 93 | "item": [ 94 | { 95 | "name": "F2.F3", 96 | "id": "F2.F3-id", 97 | "description": "", 98 | "item": [ 99 | { 100 | "name": "F2.F3.R1", 101 | "id": "F2.F3.R1-id", 102 | "event": [ 103 | { 104 | "listen": "prerequest", 105 | "script": { 106 | "type": "text/javascript", 107 | "exec": [ 108 | "var count = parseInt(postman.getEnvironmentVariable(\"count\"));", 109 | "postman.setEnvironmentVariable(\"count\", isNaN(count) ? 0 : count + 1);" 110 | ] 111 | } 112 | }, 113 | { 114 | "listen": "test", 115 | "script": { 116 | "type": "text/javascript", 117 | "exec": [ 118 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 119 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"3\";" 120 | ] 121 | } 122 | } 123 | ], 124 | "request": { 125 | "url": "https://postman-echo.com/get", 126 | "method": "GET", 127 | "header": [], 128 | "body": {}, 129 | "description": "" 130 | }, 131 | "response": [] 132 | } 133 | ] 134 | }, 135 | { 136 | "name": "F4", 137 | "description": "", 138 | "item": [] 139 | }, 140 | { 141 | "name": "F2.R1", 142 | "event": [ 143 | { 144 | "listen": "test", 145 | "script": { 146 | "type": "text/javascript", 147 | "exec": [ 148 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 149 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"4\";" 150 | ] 151 | } 152 | } 153 | ], 154 | "request": { 155 | "url": "https://postman-echo.com/get", 156 | "method": "GET", 157 | "header": [], 158 | "body": {}, 159 | "description": "" 160 | }, 161 | "response": [] 162 | } 163 | ] 164 | }, 165 | { 166 | "name": "R1", 167 | "event": [ 168 | { 169 | "listen": "prerequest", 170 | "script": { 171 | "type": "text/javascript", 172 | "exec": [ 173 | "var count = parseInt(postman.getEnvironmentVariable(\"count\"));", 174 | "postman.setEnvironmentVariable(\"count\", isNaN(count) ? 0 : count + 1);" 175 | ] 176 | } 177 | }, 178 | { 179 | "listen": "test", 180 | "script": { 181 | "type": "text/javascript", 182 | "exec": [ 183 | "tests[\"Status code is 200\"] = responseCode.code === 200;", 184 | "tests[\"Request executed in correct order\"] = postman.getEnvironmentVariable(\"count\") === \"5\";" 185 | ] 186 | } 187 | } 188 | ], 189 | "request": { 190 | "url": "https://postman-echo.com/get", 191 | "method": "GET", 192 | "header": [], 193 | "body": {}, 194 | "description": "" 195 | }, 196 | "response": [], 197 | "id": "R1-id" 198 | } 199 | ] 200 | } 201 | -------------------------------------------------------------------------------- /examples/oauth1.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "name": "Echo - OAuth 1", 5 | "_postman_id": "5c5fd0ab-84e3-64f2-b08b-f97f1f2337ae", 6 | "description": "Postman Echo is service you can use to test your REST clients and make sample API calls. It provides endpoints for `GET`, `POST`, `PUT`, various auth mechanisms and other utility endpoints.", 7 | "schema": "http://schema.getpostman.com/collection/v2/collection.json" 8 | }, 9 | "item": [ 10 | { 11 | "id": "e574612b-c19a-66e7-9685-27751bafca0d", 12 | "name": "OAuth1.0 Verify Signature", 13 | "event": [ 14 | { 15 | "listen": "test", 16 | "script": { 17 | "type": "text/javascript", 18 | "exec": "tests[\"response code is 200\"] = responseCode.code === 200;\nvar body = JSON.parse(responseBody);\ntests[\"Body contains status pass\"] = body[\"status\"] == \"pass\"" 19 | } 20 | } 21 | ], 22 | "request": { 23 | "auth": { 24 | "type": "oauth1", 25 | "oauth1": { 26 | "consumerKey": "RKCGzna7bv9YD57c", 27 | "consumerSecret": "D+EdQ-gs$-%@2Nu7", 28 | "token": "", 29 | "tokenSecret": "", 30 | "signatureMethod": "HMAC-SHA1", 31 | "timestamp": "1453890475", 32 | "nonce": "yly1UR", 33 | "version": "1.0", 34 | "realm": "", 35 | "addParamsToHeader": true, 36 | "autoAddParam": true, 37 | "addEmptyParamsToSign": false 38 | } 39 | }, 40 | "url": "https://echo.getpostman.com/oauth1", 41 | "method": "GET", 42 | "header": [ 43 | { 44 | "key": "Authorization", 45 | "value": "OAuth oauth_consumer_key=\"RKCGzna7bv9YD57c\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1453890449\",oauth_nonce=\"aT8kIM\",oauth_version=\"1.0\",oauth_signature=\"Ng8eD0bKh6LO5V0A9O6Z%2BY6D0tU%3D\"", 46 | "description": "" 47 | } 48 | ], 49 | "body": { 50 | "mode": "formdata", 51 | "formdata": [] 52 | }, 53 | "description": "OAuth1.0a is a specification that defines a protocol that can be used by one\nservice to access \"protected\" resources (endpoints) on another service. A\nmajor part of OAuth1.0 is HTTP Request Signing. This endpoint allows you to \ncheck whether the request calculation works properly in the client. \n\nThe endpoint supports the HTTP ``Authorization`` header. In case the signature\nverification fails, the endpoint provides the four debug values,\n\n* ``base_uri``\n* ``normalized_param_string``\n* ``base_string``\n* ``signing_key``\n\nFor more details about these parameters, check the [OAuth1.0a Specification](http://oauth.net/core/1.0a/)\n\nIn order to use this endpoint, you can set the following values:\n\n> Consumer Key: ``RKCGzna7bv9YD57c``\n>\n> Consumer Secret: ``D+EdQ-gs$-%@2Nu7``\n\nIf you are using Postman, also check the \"Add params to header\" and \n\"Auto add parameters\" boxes." 54 | }, 55 | "response": [] 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /**! 2 | * @license http://www.apache.org/licenses/LICENSE-2.0 3 | * 4 | * Copyright 2015 Postdot Technologies Pvt. Ltd. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 13 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and limitations under the License. 15 | */ 16 | /** 17 | * @fileOverview This is the entry point to PostmanCollection modules. The structure of the module is defined here. 18 | */ 19 | module.exports = require('./lib/index'); 20 | 21 | -------------------------------------------------------------------------------- /lib/collection/certificate-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | Url = require('./url').Url, 4 | Certificate = require('./certificate').Certificate, 5 | 6 | CertificateList; 7 | 8 | _.inherit(( 9 | 10 | /** 11 | * @constructor 12 | * @extends {PropertyList} 13 | * 14 | * @param {Object} parent - 15 | * @param {Array} list - The list of certificate representations 16 | * 17 | * @example Create a new CertificateList 18 | * var CertificateList = require('postman-collection').CertificateList, 19 | * certificateList = new CertificateList({}, [ 20 | * { 21 | * name: 'my certificate for example.com', 22 | * matches: ['https://example.com/*'], 23 | * key: { src: '/path/to/key/file' }, 24 | * cert: { src: '/path/to/certificate/file' } 25 | * }, 26 | * { 27 | * name: 'my certificate for example2.com', 28 | * matches: ['https://example2.com/*'], 29 | * key: { src: '/path/to/key/file' }, 30 | * cert: { src: '/path/to/key/file' } 31 | * } 32 | * ]); 33 | */ 34 | CertificateList = function (parent, list) { 35 | // this constructor is intended to inherit and as such the super constructor is required to be executed 36 | CertificateList.super_.call(this, Certificate, parent, list); 37 | }), PropertyList); 38 | 39 | _.assign(CertificateList.prototype, /** @lends CertificateList.prototype */ { 40 | /** 41 | * Matches the given url against the member certificates' allowed matches 42 | * and returns the certificate that can be used for the url. 43 | * 44 | * @param {String} url The url to find the certificate for 45 | * @returns {Certificate.definition=} The matched certificate 46 | */ 47 | resolveOne (url) { 48 | // url must be either string or an instance of url. 49 | if (!_.isString(url) && !Url.isUrl(url)) { 50 | return; 51 | } 52 | 53 | // find a certificate that can be applied to the url 54 | return this.find(function (certificate) { 55 | return certificate.canApplyTo(url); 56 | }); 57 | } 58 | }); 59 | 60 | _.assign(CertificateList, /** @lends CertificateList */ { 61 | /** 62 | * Defines the name of this property for internal use. 63 | * 64 | * @private 65 | * @readOnly 66 | * @type {String} 67 | */ 68 | _postman_propertyName: 'CertificateList', 69 | 70 | /** 71 | * Checks if the given object is a CertificateList 72 | * 73 | * @param {*} obj - 74 | * @returns {Boolean} 75 | */ 76 | isCertificateList: function (obj) { 77 | return Boolean(obj) && ((obj instanceof CertificateList) || 78 | _.inSuperChain(obj.constructor, '_postman_propertyName', CertificateList._postman_propertyName)); 79 | } 80 | }); 81 | 82 | module.exports = { 83 | CertificateList 84 | }; 85 | -------------------------------------------------------------------------------- /lib/collection/certificate.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | Property = require('./property').Property, 3 | PropertyBase = require('./property-base').PropertyBase, 4 | Url = require('./url').Url, 5 | UrlMatchPatternList = require('../url-pattern/url-match-pattern-list').UrlMatchPatternList, 6 | 7 | STRING = 'string', 8 | HTTPS = 'https', 9 | 10 | Certificate; 11 | 12 | /** 13 | * The following is the object representation accepted as param for the Certificate constructor. 14 | * Also the type of the object returned when {@link Property#toJSON} or {@link Property#toObjectResolved} is called on a 15 | * Certificate instance. 16 | * 17 | * @typedef Certificate.definition 18 | * @property {String} [name] A name for the certificate 19 | * @property {Array} [matches] A list of match patterns 20 | * @property {{ src: (String) }} [key] Object with path on the file system for private key file, as src 21 | * @property {{ src: (String) }} [cert] Object with path on the file system for certificate file, as src 22 | * @property {String} [passphrase] The passphrase for the certificate key 23 | * 24 | * @example JSON definition of an example certificate object 25 | * { 26 | * "name": "My certificate for example.com", 27 | * "matches": ["https://example.com/*"], 28 | * "key": { "src": "/path/to/key" }, 29 | * "cert": { "src": "/User/path/to/certificate" }, 30 | * "passphrase": "iampassphrase" 31 | * } 32 | */ 33 | _.inherit(( 34 | 35 | /** 36 | * A Certificate definition that represents the ssl certificate 37 | * to be used for an url. 38 | * Properties can then use the `.toObjectResolved` function to procure an object representation of the property with 39 | * all the variable references replaced by corresponding values. 40 | * 41 | * @constructor 42 | * @extends {Property} 43 | * 44 | * @param {Certificate.definition=} [options] Object with matches, key, cert and passphrase 45 | * 46 | * @example Create a new Certificate 47 | * 48 | * var Certificate = require('postman-collection').Certificate, 49 | * certificate = new Certificate({ 50 | * name: 'Certificate for example.com', 51 | * matches: ['example.com'], 52 | * key: { src: '/User/path/to/certificate/key' }, 53 | * cert: { src: '/User/path/to/certificate' }, 54 | * passphrase: 'iampassphrase' 55 | * }); 56 | */ 57 | Certificate = function Certificate (options) { 58 | // this constructor is intended to inherit and as such the super constructor is required to be executed 59 | Certificate.super_.apply(this, arguments); 60 | 61 | this.update(options); 62 | }), Property); 63 | 64 | _.assign(Certificate.prototype, /** @lends Certificate.prototype */ { 65 | /** 66 | * Ensure all object have id 67 | * 68 | * @private 69 | */ 70 | _postman_propertyRequiresId: true, 71 | 72 | /** 73 | * Updates the certificate with the given properties. 74 | * 75 | * @param {Certificate.definition=} [options] Object with matches, key, cert and passphrase 76 | */ 77 | update: function (options) { 78 | // return early if options is empty or invalid 79 | if (!_.isObject(options)) { 80 | return; 81 | } 82 | 83 | _.mergeDefined(this, /** @lends Certificate.prototype */ { 84 | /** 85 | * Unique identifier 86 | * 87 | * @type {String} 88 | */ 89 | id: options.id, 90 | 91 | /** 92 | * Name for user reference 93 | * 94 | * @type {String} 95 | */ 96 | name: options.name, 97 | 98 | /** 99 | * List of match pattern 100 | * 101 | * @type {UrlMatchPatternList} 102 | */ 103 | matches: options.matches && new UrlMatchPatternList({}, options.matches), 104 | 105 | /** 106 | * Private Key 107 | * 108 | * @type {{ src: (string) }} 109 | */ 110 | key: _.isObject(options.key) ? options.key : { src: options.key }, 111 | 112 | /** 113 | * Certificate 114 | * 115 | * @type {{ src: (string) }} 116 | */ 117 | cert: _.isObject(options.cert) ? options.cert : { src: options.cert }, 118 | 119 | /** 120 | * PFX or PKCS12 Certificate 121 | * 122 | * @type {{ src: (string) }} 123 | */ 124 | pfx: _.isObject(options.pfx) ? options.pfx : { src: options.pfx }, 125 | 126 | /** 127 | * passphrase 128 | * 129 | * @type {Object} 130 | */ 131 | passphrase: options.passphrase 132 | }); 133 | }, 134 | 135 | /** 136 | * Checks if the certificate can be applied to a given url 137 | * 138 | * @param {String|Url} url The url string for which the certificate is checked for match. 139 | */ 140 | canApplyTo: function (url) { 141 | if (_.isEmpty(url)) { 142 | return false; 143 | } 144 | 145 | // convert url strings to Url 146 | (typeof url === STRING) && (url = new Url(url)); 147 | 148 | // this ensures we don't proceed any further for any protocol 149 | // that is not https 150 | if (url.protocol !== HTTPS) { 151 | return false; 152 | } 153 | 154 | // test the input url against allowed matches 155 | return this.matches.test(url); 156 | }, 157 | 158 | /** 159 | * Allows the serialization of a {@link Certificate} 160 | * 161 | * This is overridden, in order to ensure that certificate contents are not accidentally serialized, 162 | * which can be a security risk. 163 | */ 164 | toJSON: function () { 165 | var obj = PropertyBase.toJSON(this); 166 | 167 | _.unset(obj, 'key.value'); 168 | _.unset(obj, 'cert.value'); 169 | _.unset(obj, 'pfx.value'); 170 | 171 | return obj; 172 | } 173 | }); 174 | 175 | _.assign(Certificate, /** @lends Certificate */ { 176 | /** 177 | * Defines the name of this property for internal use 178 | * 179 | * @private 180 | * @readOnly 181 | * @type {String} 182 | */ 183 | _postman_propertyName: 'Certificate', 184 | 185 | /** 186 | * Specify the key to be used while indexing this object 187 | * 188 | * @private 189 | * @readOnly 190 | * @type {String} 191 | */ 192 | _postman_propertyIndexKey: 'id', 193 | 194 | /** 195 | * Checks if the given object is a Certificate 196 | * 197 | * @param {*} obj - 198 | * @returns {Boolean} 199 | */ 200 | isCertificate: function (obj) { 201 | return Boolean(obj) && ((obj instanceof Certificate) || 202 | _.inSuperChain(obj.constructor, '_postman_propertyName', Certificate._postman_propertyName)); 203 | } 204 | }); 205 | 206 | module.exports = { 207 | Certificate 208 | }; 209 | -------------------------------------------------------------------------------- /lib/collection/cookie-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | Cookie = require('./cookie').Cookie, 4 | 5 | CookieList; 6 | 7 | _.inherit(( 8 | 9 | /** 10 | * Contains a list of header elements 11 | * 12 | * @constructor 13 | * @param {Object} parent - 14 | * @param {Object[]} cookies - 15 | * @extends {PropertyList} 16 | */ 17 | CookieList = function (parent, cookies) { 18 | // this constructor is intended to inherit and as such the super constructor is required to be executed 19 | CookieList.super_.call(this, Cookie, parent, cookies); 20 | }), PropertyList); 21 | 22 | // _.assign(CookieList.prototype, /** @lends CookieList.prototype */ { 23 | // }); 24 | 25 | _.assign(CookieList, /** @lends CookieList */ { 26 | /** 27 | * Defines the name of this property for internal use. 28 | * 29 | * @private 30 | * @readOnly 31 | * @type {String} 32 | */ 33 | _postman_propertyName: 'CookieList', 34 | 35 | /** 36 | * Checks if the given object is a CookieList 37 | * 38 | * @param {*} obj - 39 | * @returns {Boolean} 40 | */ 41 | isCookieList: function (obj) { 42 | return Boolean(obj) && ((obj instanceof CookieList) || 43 | _.inSuperChain(obj.constructor, '_postman_propertyName', CookieList._postman_propertyName)); 44 | } 45 | }); 46 | 47 | module.exports = { 48 | CookieList 49 | }; 50 | -------------------------------------------------------------------------------- /lib/collection/description.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | 3 | E = '', 4 | DEFAULT_MIMETYPE = 'text/plain', 5 | 6 | Description; 7 | 8 | /** 9 | * @typedef Description.definition 10 | * @property {String} content 11 | * @property {String} type 12 | */ 13 | /** 14 | * This is one of the properties that are (if provided) processed by all other properties. Any property can have an 15 | * instance of `Description` property assigned to it with the key name `description` and it should be treated as 16 | * something that "describes" the property within which it belongs. Usually this property is used to generate 17 | * documentation and other contextual information for a property in a Collection. 18 | * 19 | * @constructor 20 | * 21 | * @param {Description.definition|String} [definition] The content of the description can be passed as a string when it 22 | * is in `text/plain` format or otherwise be sent as part of an object adhering to the {@link Description.definition} 23 | * structure having `content` and `type`. 24 | * 25 | * @example Add a description to an instance of Collection 26 | * var SDK = require('postman-collection'), 27 | * Collection = SDK.Collection, 28 | * Description = SDK.Description, 29 | * mycollection; 30 | * 31 | * // create a blank collection 32 | * myCollection = new Collection(); 33 | * myCollection.description = new Description({ 34 | * content: '<h1>Hello World</h1><p>I am a Collection</p>', 35 | * type: 'text/html' 36 | * }); 37 | * 38 | * // alternatively, you could also use the `.describe` method of any property to set or update the description of the 39 | * // property. 40 | * myCollection.describe('Hey! This is a cool collection.'); 41 | */ 42 | Description = function PostmanPropertyDescription (definition) { 43 | // if the definition is a string, it implies that this is a get of URL 44 | _.isString(definition) && (definition = { 45 | content: definition, 46 | type: DEFAULT_MIMETYPE 47 | }); 48 | 49 | // populate the description 50 | definition && this.update(definition); 51 | }; 52 | 53 | _.assign(Description.prototype, /** @lends Description.prototype */ { 54 | /** 55 | * Updates the content of this description property. 56 | * 57 | * @param {String|Description.definition} content - 58 | * @param {String=} [type] - 59 | * @todo parse version of description 60 | */ 61 | update (content, type) { 62 | _.isObject(content) && ((type = content.type), (content = content.content)); 63 | _.assign(this, /** @lends Description.prototype */ { 64 | /** 65 | * The raw content of the description 66 | * 67 | * @type {String} 68 | */ 69 | content: content, 70 | 71 | /** 72 | * The mime-type of the description. 73 | * 74 | * @type {String} 75 | */ 76 | type: type || DEFAULT_MIMETYPE 77 | }); 78 | }, 79 | 80 | /** 81 | * Returns stringified Description. 82 | * 83 | * @returns {String} 84 | */ 85 | toString () { 86 | return this.content || E; 87 | }, 88 | 89 | /** 90 | * Creates a JSON representation of the Description (as a plain Javascript object). 91 | * 92 | * @returns {{content: *, type: *, version: (String|*)}} 93 | */ 94 | toJSON () { 95 | return { 96 | content: this.content, 97 | type: this.type 98 | }; 99 | } 100 | }); 101 | 102 | _.assign(Description, /** @lends Description */ { 103 | /** 104 | * Defines the name of this property for internal use. 105 | * 106 | * @private 107 | * @readOnly 108 | * @type {String} 109 | */ 110 | _postman_propertyName: 'Description', 111 | 112 | /** 113 | * Checks whether a property is an instance of Description object. 114 | * 115 | * @param {*} obj - 116 | * @returns {Boolean} 117 | */ 118 | isDescription: function (obj) { 119 | return Boolean(obj) && ((obj instanceof Description) || 120 | _.inSuperChain(obj.constructor, '_postman_propertyName', Description._postman_propertyName)); 121 | } 122 | }); 123 | 124 | module.exports = { 125 | Description 126 | }; 127 | -------------------------------------------------------------------------------- /lib/collection/event-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | Event = require('./event').Event, 4 | 5 | EventList; 6 | 7 | _.inherit(( 8 | 9 | /** 10 | * A type of {@link PropertyList}, EventList handles resolving events from parents. If an {@link ItemGroup} contains 11 | * a set of events, each {@link Item} in that group will inherit those events from its parent, and so on. 12 | * 13 | * @constructor 14 | * @param {Object} parent - 15 | * @param {Object[]} populate - 16 | * @extends {PropertyList} 17 | * 18 | * This is useful when we need to have a common test across all requests. 19 | */ 20 | EventList = function PostmanEventList (parent, populate) { 21 | // this constructor is intended to inherit and as such the super constructor is required to be executed 22 | EventList.super_.call(this, Event, parent, populate); 23 | }), PropertyList); 24 | 25 | _.assign(EventList.prototype, /** @lends EventList.prototype */ { 26 | /** 27 | * Returns an array of listeners filtered by the listener name 28 | * 29 | * @note 30 | * If one needs to access disabled events, use {@link PropertyList#all} or 31 | * any other similar {@link PropertyList} method. 32 | * 33 | * @param {String} name - 34 | * @returns {Array} 35 | */ 36 | listeners (name) { 37 | var all; 38 | 39 | // we first procure all matching events from this list 40 | all = this.listenersOwn(name); 41 | 42 | this.eachParent(function (parent) { 43 | var parentEvents; 44 | 45 | // we check that the parent is not immediate mother. then we check whether the non immediate mother has a 46 | // valid `events` store and only if this store has events with specified listener, we push them to the 47 | // array we are compiling for return 48 | (parent !== this.__parent) && EventList.isEventList(parent.events) && 49 | (parentEvents = parent.events.listenersOwn(name)) && parentEvents.length && 50 | all.unshift.apply(all, parentEvents); // eslint-disable-line prefer-spread 51 | }, this); 52 | 53 | return all; 54 | }, 55 | 56 | /** 57 | * Returns all events with specific listeners only within this list. Refer to {@link EventList#listeners} for 58 | * procuring all inherited events 59 | * 60 | * @param {string} name - 61 | * @returns {Array} 62 | */ 63 | listenersOwn (name) { 64 | return this.filter(function (event) { 65 | return (!event.disabled && event.listen === name); 66 | }); 67 | } 68 | }); 69 | 70 | _.assign(EventList, /** @lends EventList */ { 71 | /** 72 | * Defines the name of this property for internal use. 73 | * 74 | * @private 75 | * @readOnly 76 | * @type {String} 77 | */ 78 | _postman_propertyName: 'EventList', 79 | 80 | /** 81 | * Checks if the given object is an EventList. 82 | * 83 | * @param {*} obj - 84 | * @returns {Boolean} 85 | */ 86 | isEventList: function (obj) { 87 | return Boolean(obj) && ((obj instanceof EventList) || 88 | _.inSuperChain(obj.constructor, '_postman_propertyName', EventList._postman_propertyName)); 89 | } 90 | }); 91 | 92 | module.exports = { 93 | EventList 94 | }; 95 | -------------------------------------------------------------------------------- /lib/collection/event.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | Property = require('./property').Property, 3 | Script = require('./script').Script, 4 | 5 | Event; 6 | 7 | /** 8 | * @typedef Event.definition 9 | * @property {String} listen The event-name that this script will be called for. Usually either "test" or "prerequest" 10 | * @property {Script|String} script A {@link Script} instance that will be executed on this event. In case of a 11 | * string, a new {@link Script} is created. 12 | * @example Constructing an event 13 | * var Event = require('postman-collection').Event, 14 | * rawEvent = { 15 | * listen: 'test', 16 | * script: 'tests["response code is 401"] = responseCode.code === 401' 17 | * }, 18 | * myEvent; 19 | * myEvent = new Event(rawEvent); 20 | */ 21 | _.inherit(( 22 | 23 | /** 24 | * A Postman event definition that refers to an event to be listened to and a script reference or definition to be 25 | * executed. 26 | * 27 | * @constructor 28 | * @extends {Property} 29 | * 30 | * @param {Event.definition} definition Pass the initial definition of the event as the options parameter. 31 | */ 32 | Event = function PostmanEvent (definition) { 33 | // this constructor is intended to inherit and as such the super constructor is required to be executed 34 | Event.super_.call(this, definition); 35 | // set initial values of this event 36 | definition && this.update(definition); 37 | }), Property); 38 | 39 | _.assign(Event.prototype, /** @lends Event.prototype */ { 40 | /** 41 | * Update an event. 42 | * 43 | * @param {Event.definition} definition - 44 | */ 45 | update (definition) { 46 | if (!definition) { 47 | return; 48 | } 49 | 50 | var result, 51 | script = definition.script; 52 | 53 | if (Script.isScript(script)) { 54 | result = script; 55 | } 56 | else if (_.isArray(script) || _.isString(script)) { 57 | result = new Script({ exec: script }); 58 | } 59 | else if (_.isObject(script)) { 60 | result = new Script(script); 61 | } 62 | 63 | _.mergeDefined(this, /** @lends Event.prototype */ { 64 | /** 65 | * Name of the event that this instance is intended to listen to. 66 | * 67 | * @type {String} 68 | */ 69 | listen: _.isString(definition.listen) ? definition.listen : undefined, 70 | 71 | /** 72 | * The script that is to be executed when this event is triggered. 73 | * 74 | * @type {Script} 75 | */ 76 | script: result 77 | }); 78 | } 79 | }); 80 | 81 | _.assign(Event, /** @lends Event */ { 82 | 83 | /** 84 | * Defines the name of this property for internal use. 85 | * 86 | * @private 87 | * @readOnly 88 | * @type {String} 89 | */ 90 | _postman_propertyName: 'Event', 91 | 92 | /** 93 | * Check whether an object is an instance of {@link Event}. 94 | * 95 | * @param {*} obj - 96 | * @returns {Boolean} 97 | */ 98 | isEvent: function isPostmanEvent (obj) { 99 | return Boolean(obj) && ((obj instanceof Event) || 100 | _.inSuperChain(obj.constructor, '_postman_propertyName', Event._postman_propertyName)); 101 | } 102 | }); 103 | 104 | module.exports = { 105 | Event 106 | }; 107 | -------------------------------------------------------------------------------- /lib/collection/form-param.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | Property = require('./property').Property, 3 | PropertyBase = require('./property-base').PropertyBase, 4 | 5 | FormParam; 6 | 7 | /** 8 | * @typedef FormParam.definition 9 | * @property {String} key The name ("key") of the form data parameter. 10 | * @property {String} value The value of the parameter. 11 | */ 12 | _.inherit(( 13 | 14 | /** 15 | * Represents a Form Data parameter, which can exist in request body. 16 | * 17 | * @constructor 18 | * @param {FormParam.definition} options Pass the initial definition of the form data parameter. 19 | */ 20 | FormParam = function PostmanFormParam (options = {}) { 21 | FormParam.super_.apply(this, arguments); 22 | 23 | this.key = options.key || ''; 24 | this.value = options.value || ''; 25 | this.type = options.type; 26 | this.src = options.src; 27 | this.contentType = options.contentType; 28 | this.fileName = options.fileName; 29 | }), Property); 30 | 31 | _.assign(FormParam.prototype, /** @lends FormParam.prototype */ { 32 | /** 33 | * Converts the FormParameter to a single param string. 34 | * 35 | * @returns {String} 36 | */ 37 | toString () { 38 | return this.key + '=' + this.value; 39 | }, 40 | 41 | /** 42 | * Returns the value of the form parameter (if any). 43 | * 44 | * @returns {*|String} 45 | */ 46 | valueOf () { 47 | return this.value; // can be multiple types, so just return whatever we have instead of being too clever 48 | }, 49 | 50 | /** 51 | * Convert the form-param to JSON compatible plain object. 52 | * 53 | * @returns {Object} 54 | */ 55 | toJSON () { 56 | var obj = PropertyBase.toJSON(this); 57 | 58 | // remove value from file param if it's empty or non-string (can be non-serializable ReadStream) 59 | if (obj.type === 'file' && (typeof obj.value !== 'string' || !obj.value)) { 60 | _.unset(obj, 'value'); 61 | } 62 | 63 | return obj; 64 | } 65 | }); 66 | 67 | _.assign(FormParam, /** @lends FormParam */ { 68 | 69 | /** 70 | * Defines the name of this property for internal use. 71 | * 72 | * @private 73 | * @readOnly 74 | * @type {String} 75 | */ 76 | _postman_propertyName: 'FormParam', 77 | 78 | /** 79 | * Declare the list index key, so that property lists of form parameters work correctly 80 | * 81 | * @type {String} 82 | */ 83 | _postman_propertyIndexKey: 'key', 84 | 85 | /** 86 | * Form params can have multiple values, so set this to true. 87 | * 88 | * @type {Boolean} 89 | */ 90 | _postman_propertyAllowsMultipleValues: true, 91 | 92 | /** 93 | * Parse a form data string into an array of objects, where each object contains a key and a value. 94 | * 95 | * @todo implement this, not implemented yet. 96 | * @param formdata {String} 97 | * @returns {Array} 98 | */ 99 | parse: _.noop 100 | }); 101 | 102 | module.exports = { 103 | FormParam 104 | }; 105 | -------------------------------------------------------------------------------- /lib/collection/header-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | Header = require('./header').Header, 4 | 5 | PROP_NAME = '_postman_propertyName', 6 | 7 | HeaderList; 8 | 9 | _.inherit(( 10 | 11 | /** 12 | * Contains a list of header elements 13 | * 14 | * @constructor 15 | * @param {Object} parent - 16 | * @param {Header[]} headers - 17 | * @extends {PropertyList} 18 | */ 19 | HeaderList = function (parent, headers) { 20 | // this constructor is intended to inherit and as such the super constructor is required to be executed 21 | HeaderList.super_.call(this, Header, parent, headers); 22 | }), PropertyList); 23 | 24 | _.assign(HeaderList.prototype, /** @lends HeaderList.prototype */ { 25 | /** 26 | * Gets size of a list of headers excluding standard header prefix. 27 | * 28 | * @returns {Number} 29 | */ 30 | contentSize () { 31 | if (!this.count()) { return 0; } 32 | 33 | return Header.unparse(this).length; 34 | } 35 | }); 36 | 37 | _.assign(HeaderList, /** @lends HeaderList */ { 38 | /** 39 | * Defines the name of this property for internal use. 40 | * 41 | * @private 42 | * @readOnly 43 | * @type {String} 44 | */ 45 | _postman_propertyName: 'HeaderList', 46 | 47 | /** 48 | * Checks if the given object is a HeaderList 49 | * 50 | * @param {*} obj - 51 | * @returns {Boolean} 52 | */ 53 | isHeaderList: function (obj) { 54 | return Boolean(obj) && ((obj instanceof HeaderList) || 55 | _.inSuperChain(obj.constructor, PROP_NAME, HeaderList._postman_propertyName)); 56 | } 57 | }); 58 | 59 | module.exports = { 60 | HeaderList 61 | }; 62 | -------------------------------------------------------------------------------- /lib/collection/property-base.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | 3 | __PARENT = '__parent', 4 | 5 | PropertyBase; // constructor 6 | 7 | /** 8 | * @typedef PropertyBase.definition 9 | * @property {String|Description} [description] 10 | */ 11 | /** 12 | * Base of all properties in Postman Collection. It defines the root for all standalone properties for postman 13 | * collection. 14 | * 15 | * @constructor 16 | * @param {PropertyBase.definition} definition - 17 | */ 18 | PropertyBase = function PropertyBase (definition) { 19 | // In case definition object is missing, there is no point moving forward. Also if the definition is basic string 20 | // we do not need to do anything with it. 21 | if (!definition || typeof definition === 'string') { return; } 22 | 23 | // call the meta extraction functions to create the object where all keys that are prefixed with underscore can be 24 | // stored. more details on that can be retrieved from the propertyExtractMeta function itself. 25 | // @todo: make this a closed function to do getter and setter which is non enumerable 26 | var src = definition && definition.info || definition, 27 | meta = _(src).pickBy(PropertyBase.propertyIsMeta).mapKeys(PropertyBase.propertyUnprefixMeta).value(); 28 | 29 | if (_.keys(meta).length) { 30 | this._ = _.isObject(this._) ? 31 | /* istanbul ignore next */ 32 | _.mergeDefined(this._, meta) : 33 | meta; 34 | } 35 | }; 36 | 37 | _.assign(PropertyBase.prototype, /** @lends PropertyBase.prototype */ { 38 | 39 | /** 40 | * Invokes the given iterator for every parent in the parent chain of the given element. 41 | * 42 | * @param {Object} options - A set of options for the parent chain traversal. 43 | * @param {?Boolean} [options.withRoot=false] - Set to true to include the collection object as well. 44 | * @param {Function} iterator - The function to call for every parent in the ancestry chain. 45 | * @todo Cache the results 46 | */ 47 | forEachParent (options, iterator) { 48 | _.isFunction(options) && (iterator = options, options = {}); 49 | if (!_.isFunction(iterator) || !_.isObject(options)) { return; } 50 | 51 | var parent = this.parent(), 52 | grandparent = parent && _.isFunction(parent.parent) && parent.parent(); 53 | 54 | while (parent && (grandparent || options.withRoot)) { 55 | iterator(parent); 56 | parent = grandparent; 57 | grandparent = grandparent && _.isFunction(grandparent.parent) && grandparent.parent(); 58 | } 59 | }, 60 | 61 | /** 62 | * Tries to find the given property locally, and then proceeds to lookup in each parent, 63 | * going up the chain as necessary. Lookup will continue until `customizer` returns a truthy value. If used 64 | * without a customizer, the lookup will stop at the first parent that contains the property. 65 | * 66 | * @param {String} property - 67 | * @param {Function} [customizer] - 68 | * @returns {*|undefined} 69 | */ 70 | findInParents (property, customizer) { 71 | var owner = this.findParentContaining(property, customizer); 72 | 73 | return owner ? owner[property] : undefined; 74 | }, 75 | 76 | /** 77 | * Looks up the closest parent which has a truthy value for the given property. Lookup will continue 78 | * until `customizer` returns a truthy value. If used without a customizer, 79 | * the lookup will stop at the first parent that contains the property. 80 | * 81 | * @private 82 | * @param {String} property - 83 | * @param {Function} [customizer] - 84 | * @returns {PropertyBase|undefined} 85 | */ 86 | findParentContaining (property, customizer) { 87 | var parent = this; 88 | 89 | // if customizer is present test with it 90 | if (customizer) { 91 | customizer = customizer.bind(this); 92 | 93 | do { 94 | // else check for existence 95 | if (customizer(parent)) { 96 | return parent; 97 | } 98 | 99 | parent = parent.__parent; 100 | } while (parent); 101 | } 102 | 103 | // else check for existence 104 | else { 105 | do { 106 | if (parent[property]) { 107 | return parent; 108 | } 109 | 110 | parent = parent.__parent; 111 | } while (parent); 112 | } 113 | }, 114 | 115 | /** 116 | * Returns the JSON representation of a property, which conforms to the way it is defined in a collection. 117 | * You can use this method to get the instantaneous representation of any property, including a {@link Collection}. 118 | */ 119 | toJSON () { 120 | return _.reduce(this, function (accumulator, value, key) { 121 | if (value === undefined) { // true/false/null need to be preserved. 122 | return accumulator; 123 | } 124 | 125 | // Handle plurality of PropertyLists in the SDK vs the exported JSON. 126 | // Basically, removes the trailing "s" from key if the value is a property list. 127 | // eslint-disable-next-line @stylistic/js/max-len 128 | if (value && value._postman_propertyIsList && !value._postman_proprtyIsSerialisedAsPlural && _.endsWith(key, 's')) { 129 | key = key.slice(0, -1); 130 | } 131 | 132 | // Handle 'PropertyBase's 133 | if (value && _.isFunction(value.toJSON)) { 134 | accumulator[key] = value.toJSON(); 135 | 136 | return accumulator; 137 | } 138 | 139 | // Handle Strings 140 | if (_.isString(value)) { 141 | accumulator[key] = value; 142 | 143 | return accumulator; 144 | } 145 | 146 | // Everything else 147 | accumulator[key] = _.cloneElement(value); 148 | 149 | return accumulator; 150 | }, {}); 151 | }, 152 | 153 | /** 154 | * Returns the meta keys associated with the property 155 | * 156 | * @returns {*} 157 | */ 158 | meta () { 159 | return arguments.length ? _.pick(this._, Array.prototype.slice.apply(arguments)) : _.cloneDeep(this._); 160 | }, 161 | 162 | /** 163 | * Returns the parent of item 164 | * 165 | * @returns {*|undefined} 166 | */ 167 | parent () { 168 | let parent = this.__parent; 169 | 170 | // if the parent is a list, return the grandparent 171 | if (parent && parent._postman_propertyIsList) { 172 | parent = parent.__parent || parent; 173 | } 174 | 175 | return parent || undefined; 176 | }, 177 | 178 | /** 179 | * Accepts an object and sets it as the parent of the current property. 180 | * 181 | * @param {Object} parent The object to set as parent. 182 | * @private 183 | */ 184 | setParent (parent) { 185 | _.assignHidden(this, __PARENT, parent); 186 | } 187 | }); 188 | 189 | _.assign(PropertyBase, /** @lends PropertyBase */ { 190 | 191 | /** 192 | * Defines the name of this property for internal use. 193 | * 194 | * @private 195 | * @readOnly 196 | * @type {String} 197 | */ 198 | _postman_propertyName: 'PropertyBase', 199 | 200 | /** 201 | * Filter function to check whether a key starts with underscore or not. These usually are the meta properties. It 202 | * returns `true` if the criteria is matched. 203 | * 204 | * @param {*} value - 205 | * @param {String} key - 206 | * 207 | * @returns {boolean} 208 | */ 209 | propertyIsMeta: function (value, key) { 210 | return _.startsWith(key, '_') && (key !== '_'); 211 | }, 212 | 213 | /** 214 | * Map function that removes the underscore prefix from an object key. 215 | * 216 | * @param {*} value - 217 | * @param {String} key - 218 | * @returns {String} 219 | */ 220 | propertyUnprefixMeta: function (value, key) { 221 | return _.trimStart(key, '_'); 222 | }, 223 | 224 | /** 225 | * Static function which allows calling toJSON() on any object. 226 | * 227 | * @param {Object} obj - 228 | * @returns {*} 229 | */ 230 | toJSON: function (obj) { 231 | return PropertyBase.prototype.toJSON.call(obj); 232 | } 233 | }); 234 | 235 | module.exports = { 236 | PropertyBase 237 | }; 238 | -------------------------------------------------------------------------------- /lib/collection/proxy-config-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | ProxyConfig = require('./proxy-config').ProxyConfig, 4 | Url = require('./url').Url, 5 | 6 | ProxyConfigList; 7 | 8 | _.inherit(( 9 | 10 | /** 11 | * @constructor 12 | * @extends {PropertyList} 13 | * 14 | * @param {Object} parent - 15 | * @param {Array} populate The list of proxy objects 16 | * 17 | * @example Create a new ProxyConfigList 18 | * var ProxyConfigList = require('postman-collection').ProxyConfigList, 19 | * myProxyConfig = new ProxyConfigList({}, [ 20 | * {match: 'https://example.com/*', host: 'proxy.com', port: 8080, tunnel: true}, 21 | * {match: 'http+https://example2.com/*', host: 'proxy2.com'}, 22 | * ]); 23 | */ 24 | ProxyConfigList = function PostmanProxyConfigList (parent, populate) { 25 | // this constructor is intended to inherit and as such the super constructor is required to be executed 26 | ProxyConfigList.super_.call(this, ProxyConfig, parent, populate); 27 | }), PropertyList); 28 | 29 | _.assign(ProxyConfigList.prototype, /** @lends ProxyConfigList.prototype */ { 30 | /** 31 | * Matches and gets the proxy config for the particular url. 32 | * 33 | * @returns {ProxyConfig.definition} The matched proxyConfig object 34 | * @param {URL=} [url] The url for which the proxy config needs to be fetched 35 | */ 36 | resolve (url) { 37 | // url must be either string or an instance of url. 38 | if (!_.isString(url) && !Url.isUrl(url)) { 39 | return; 40 | } 41 | 42 | // @todo - use a fixed-length cacheing of regexes in future 43 | return this.find(function (proxyConfig) { 44 | return !proxyConfig.disabled && proxyConfig.test(url); 45 | }); 46 | } 47 | }); 48 | 49 | _.assign(ProxyConfigList, /** @lends ProxyConfigList */ { 50 | /** 51 | * Defines the name of this property for internal use. 52 | * 53 | * @private 54 | * @readOnly 55 | * @type {String} 56 | * 57 | * @note that this is directly accessed only in case of ProxyConfigList from _.findValue lodash util mixin 58 | */ 59 | _postman_propertyName: 'ProxyConfigList', 60 | 61 | /** 62 | * Checks whether an object is a ProxyConfigList 63 | * 64 | * @param {*} obj - 65 | * @returns {Boolean} 66 | */ 67 | isProxyConfigList: function (obj) { 68 | return Boolean(obj) && ((obj instanceof ProxyConfigList) || 69 | _.inSuperChain(obj.constructor, '_postman_propertyName', ProxyConfigList._postman_propertyName)); 70 | } 71 | }); 72 | 73 | module.exports = { 74 | ProxyConfigList 75 | }; 76 | -------------------------------------------------------------------------------- /lib/collection/request-auth.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | Property = require('./property').Property, 3 | VariableList = require('./variable-list').VariableList, 4 | 5 | RequestAuth; 6 | 7 | /** 8 | * This defines the definition of the authentication method to be used. 9 | * 10 | * @typedef RequestAuth.definition 11 | * @property {String=} type The Auth type to use. Check the names in {@link AuthTypes} 12 | * 13 | * @example Sample auth definition for Basic Auth 14 | * { 15 | * "type": "basic", 16 | * "basic": [ 17 | * { "key": "username", "value": "postman" }, 18 | * { "key": "password", "value": "secrets" } 19 | * ] 20 | * } 21 | */ 22 | _.inherit(( 23 | 24 | /** 25 | * A Postman Auth definition that comprehensively represents different types of auth mechanisms available. 26 | * 27 | * @constructor 28 | * @extends {Property} 29 | * 30 | * @param {RequestAuth.definition} options Pass the initial definition of the Auth. 31 | * @param {Property|PropertyList=} [parent] optionally pass the parent of this auth. aides variable resolution. 32 | * 33 | * @example Creating a request with two auth data and one selected 34 | * var auth = new RequestAuth({ 35 | * type: 'digest', 36 | * 37 | * basic: [ 38 | * { key: "username", value: "postman" }, 39 | * { key: "password", value: "secrets" } 40 | * ], 41 | * digest: [ 42 | * { key: "nonce", value: "aef54cde" }, 43 | * { key: "realm", value: "items.x" } 44 | * ] 45 | * }); 46 | * 47 | * // change the selected auth 48 | * auth.use('basic'); 49 | */ 50 | RequestAuth = function PostmanRequestAuth (options, parent) { 51 | // this constructor is intended to inherit and as such the super constructor is required to be executed 52 | RequestAuth.super_.call(this, options); 53 | 54 | // set the parent 55 | parent && this.setParent(parent); 56 | 57 | // set the type, if passed via options 58 | if (_.has(options, 'type')) { 59 | this.use(options.type); 60 | } 61 | 62 | // load all possible auth parameters from options 63 | _.forEach(_.omit(options, 'type'), this.update.bind(this)); 64 | }), Property); 65 | 66 | _.assign(RequestAuth.prototype, /** @lends RequestAuth.prototype */ { 67 | /** 68 | * Update the parameters of a specific authentication type. If none is provided then it uses the one marked as to be 69 | * used. 70 | * 71 | * @param {VariableList|Array|Object} options - 72 | * @param {String=} [type=this.type] - 73 | */ 74 | update (options, type) { 75 | // update must have options 76 | if (!_.isObject(options)) { return; } 77 | // choose default from existing type if not provided 78 | if (!type) { type = this.type; } 79 | // validate type parameter and return in case type is not valid. 80 | if (!RequestAuth.isValidType(type)) { return; } 81 | 82 | var parameters = this[type]; 83 | 84 | // in case the type holder is not created, we create one and send the population variables 85 | if (!VariableList.isVariableList(parameters)) { 86 | // @todo optimise the handling of legacy object type auth parameters 87 | parameters = this[type] = new VariableList(this); 88 | parameters._postman_requestAuthType = type; 89 | } 90 | 91 | // we simply assimilate the new options either it is an array or an object 92 | if (_.isArray(options) || VariableList.isVariableList(options)) { 93 | parameters.assimilate(options); 94 | } 95 | else { 96 | parameters.syncFromObject(options, false, false); // params: no need to track and no need to prune 97 | } 98 | }, 99 | 100 | /** 101 | * Sets the authentication type to be used by this item. 102 | * 103 | * @param {String} type - 104 | * @param {VariableList|Array|Object} options - note that options set here would replace all existing 105 | * options for the particular auth 106 | */ 107 | use (type, options) { 108 | if (!RequestAuth.isValidType(type)) { return; } 109 | 110 | this.type = type; // set the type 111 | 112 | var parameters = this[type]; 113 | 114 | if (!VariableList.isVariableList(parameters)) { 115 | parameters = this[type] = new VariableList(this); 116 | } 117 | 118 | // we simply assimilate the new options either it is an array or an object 119 | if (_.isArray(options) || VariableList.isVariableList(options)) { 120 | parameters.assimilate(options); 121 | } 122 | else { 123 | parameters.syncFromObject(options, false, false); // params: no need to track and no need to prune 124 | } 125 | }, 126 | 127 | /** 128 | * @private 129 | * @deprecated discontinued in v4.0 130 | */ 131 | current () { 132 | throw new Error('`Request#current` has been discontinued, use `Request#parameters` instead.'); 133 | }, 134 | 135 | /** 136 | * Returns the parameters of the selected auth type 137 | * 138 | * @returns {VariableList} 139 | */ 140 | parameters () { 141 | return this[this.type]; 142 | }, 143 | 144 | /** 145 | * Clears the definition of an auth type. 146 | * 147 | * @param {String} type - 148 | */ 149 | clear (type) { 150 | if (!(RequestAuth.isValidType(type) && VariableList.isVariableList(this[type]))) { 151 | return; 152 | } 153 | 154 | // clear the variable list 155 | this[type].clear(); 156 | 157 | // if it is not a currently selected auth type, do not delete the variable list, but simply delete it 158 | if (type !== this.type) { 159 | delete this[type]; 160 | } 161 | } 162 | }); 163 | 164 | _.assign(RequestAuth, /** @lends RequestAuth */ { 165 | /** 166 | * Defines the name of this property for internal use. 167 | * 168 | * @private 169 | * @readOnly 170 | * @type {String} 171 | */ 172 | _postman_propertyName: 'RequestAuth', 173 | 174 | /** 175 | * Determines whether an authentication type name is valid or not 176 | * 177 | * @param {String} type - 178 | * @returns {Boolean} 179 | */ 180 | isValidType: function (type) { 181 | // no auth name can be "type", else will have namespace collision with type selector 182 | return _.isString(type) && (type !== 'type'); 183 | } 184 | }); 185 | 186 | module.exports = { 187 | RequestAuth 188 | }; 189 | -------------------------------------------------------------------------------- /lib/collection/script.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | Property = require('./property').Property, 3 | Url = require('./url').Url, 4 | 5 | Script, 6 | 7 | SCRIPT_NEWLINE_PATTERN = /\r?\n/g; 8 | 9 | /** 10 | * A map of package names to the IDs of the packages 11 | * 12 | * @typedef {Object.} Packages 13 | */ 14 | 15 | _.inherit(( 16 | 17 | /** 18 | * Postman scripts that are executed upon events on a collection / request such as test and pre request. 19 | * 20 | * @constructor 21 | * @extends {Property} 22 | * 23 | * @param {Object} options - 24 | */ 25 | Script = function PostmanScript (options) { 26 | // this constructor is intended to inherit and as such the super constructor is required to be executed 27 | Script.super_.apply(this, arguments); 28 | 29 | options && this.update(options); 30 | }), Property); 31 | 32 | _.assign(Script.prototype, /** @lends Script.prototype */ { 33 | /** 34 | * Defines whether this property instances requires an id 35 | * 36 | * @private 37 | * @readOnly 38 | * @type {Boolean} 39 | */ 40 | _postman_propertyRequiresId: true, 41 | 42 | /** 43 | * Converts the script lines array to a single source string. 44 | * 45 | * @returns {String} 46 | */ 47 | toSource: function () { 48 | return this.exec ? this.exec.join('\n') : undefined; 49 | }, 50 | 51 | /** 52 | * Updates the properties of a Script. 53 | * 54 | * @param {Object} [options] - 55 | * @param {String} [options.type] Script type 56 | * @param {String} [options.src] Script source url 57 | * @param {String[]|String} [options.exec] Script to execute 58 | * @param {Packages} [options.packages] Packages required by the script 59 | */ 60 | update: function (options) { 61 | // no splitting is being done here, as string scripts are split right before assignment below anyway 62 | (_.isString(options) || _.isArray(options)) && (options = { exec: options }); 63 | 64 | if (!options) { return; } // in case definition object is missing, there is no point moving forward 65 | 66 | // create the request property 67 | /** 68 | * @augments {Script.prototype} 69 | * @type {string} 70 | */ 71 | this.type = options.type || 'text/javascript'; 72 | 73 | /** 74 | * The packages required by the script 75 | * 76 | * @type {Packages} 77 | */ 78 | this.packages = options.packages; 79 | 80 | _.has(options, 'src') && ( 81 | 82 | /** 83 | * @augments {Script.prototype} 84 | * @type {Url} 85 | */ 86 | this.src = new Url(options.src) 87 | ); 88 | 89 | if (!this.src && _.has(options, 'exec')) { 90 | /** 91 | * @augments {Script.prototype} 92 | * @type {Array} 93 | */ 94 | this.exec = _.isString(options.exec) ? options.exec.split(SCRIPT_NEWLINE_PATTERN) : 95 | _.isArray(options.exec) ? options.exec : undefined; 96 | } 97 | }, 98 | 99 | /** 100 | * Checks if the script is empty i.e does not have any code to execute. 101 | * 102 | * @returns {Boolean} 103 | */ 104 | isEmpty: function () { 105 | return _.isEmpty(_.trim(this.toSource())); 106 | } 107 | }); 108 | 109 | _.assign(Script, /** @lends Script */ { 110 | /** 111 | * Defines the name of this property for internal use. 112 | * 113 | * @private 114 | * @readOnly 115 | * @type {String} 116 | */ 117 | _postman_propertyName: 'Script', 118 | 119 | /** 120 | * Check whether an object is an instance of {@link ItemGroup}. 121 | * 122 | * @param {*} obj - 123 | * @returns {Boolean} 124 | */ 125 | isScript: function (obj) { 126 | return Boolean(obj) && ((obj instanceof Script) || 127 | _.inSuperChain(obj.constructor, '_postman_propertyName', Script._postman_propertyName)); 128 | } 129 | }); 130 | 131 | module.exports = { 132 | Script 133 | }; 134 | -------------------------------------------------------------------------------- /lib/collection/variable-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('./property-list').PropertyList, 3 | Property = require('./property').Property, 4 | Variable = require('./variable').Variable, 5 | 6 | VariableList; 7 | 8 | _.inherit(( 9 | 10 | /** 11 | * @constructor 12 | * @extends {PropertyList} 13 | * 14 | * @param {Property} parent - 15 | * @param {Object|Array} populate - 16 | */ 17 | VariableList = function PostmanVariableList (parent, populate) { 18 | // this constructor is intended to inherit and as such the super constructor is required to be executed 19 | VariableList.super_.call(this, Variable, parent, populate); 20 | }), PropertyList); 21 | 22 | _.assign(VariableList.prototype, /** @lends VariableList.prototype */ { 23 | /** 24 | * Replaces the variable tokens inside a string with its actual values. 25 | * 26 | * @param {String} str - 27 | * @param {Object} [overrides] - additional objects to lookup for variable values 28 | * @returns {String} 29 | */ 30 | replace (str, overrides) { 31 | return Property.replaceSubstitutions(str, this, overrides); 32 | }, 33 | 34 | /** 35 | * Recursively replace strings in an object with instances of variables. Note that it clones the original object. If 36 | * the `mutate` param is set to true, then it replaces the same object instead of creating a new one. 37 | * 38 | * @param {Array|Object} obj - 39 | * @param {?Array=} [overrides] - additional objects to lookup for variable values 40 | * @param {Boolean=} [mutate=false] - 41 | * @returns {Array|Object} 42 | */ 43 | substitute (obj, overrides, mutate) { 44 | var resolutionQueue = [], // we use this to store the queue of variable hierarchy 45 | 46 | // this is an intermediate object to stimulate a property (makes the do-while loop easier) 47 | variableSource = { 48 | variables: this, 49 | __parent: this.__parent 50 | }; 51 | 52 | do { // iterate and accumulate as long as you find `.variables` in parent tree 53 | variableSource.variables && resolutionQueue.push(variableSource.variables); 54 | variableSource = variableSource.__parent; 55 | } while (variableSource); 56 | 57 | variableSource = null; // cautious cleanup 58 | 59 | return Property.replaceSubstitutionsIn(obj, _.union(resolutionQueue, overrides), mutate); 60 | }, 61 | 62 | /** 63 | * Using this function, one can sync the values of this variable list from a reference object. 64 | * 65 | * @param {Object} obj - 66 | * @param {Boolean=} track - 67 | * @param {Boolean} [prune=true] - 68 | * 69 | * @returns {Object} 70 | */ 71 | syncFromObject (obj, track, prune) { 72 | var list = this, 73 | ops = track && { 74 | created: [], 75 | updated: [], 76 | deleted: [] 77 | }, 78 | indexer = list._postman_listIndexKey, 79 | tmp; 80 | 81 | if (!_.isObject(obj)) { return ops; } 82 | 83 | // ensure that all properties in the object is updated in this list 84 | _.forOwn(obj, function (value, key) { 85 | // we need to create new variable if exists or update existing 86 | if (list.has(key)) { 87 | list.one(key).set(value); 88 | ops && ops.updated.push(key); 89 | } 90 | else { 91 | tmp = { value }; 92 | tmp[indexer] = key; 93 | list.add(tmp); 94 | tmp = null; 95 | ops && ops.created.push(key); 96 | } 97 | }); 98 | 99 | // now remove any variable that is not in source object 100 | // @note - using direct `this.reference` list of keys here so that we can mutate the list while iterating 101 | // on it 102 | if (prune !== false) { 103 | _.forEach(list.reference, function (value, key) { 104 | if (_.has(obj, key)) { return; } // de not delete if source obj has this variable 105 | list.remove(key); // use PropertyList functions to remove so that the .members array is cleared too 106 | ops && ops.deleted.push(key); 107 | }); 108 | } 109 | 110 | return ops; 111 | }, 112 | 113 | /** 114 | * Transfer all variables from this list to an object 115 | * 116 | * @param {Object=} [obj] - 117 | * @returns {Object} 118 | */ 119 | syncToObject (obj) { 120 | var list = this; 121 | 122 | // in case user did not provide an object to mutate, create a new one 123 | !_.isObject(obj) && (obj = {}); 124 | 125 | // delete extra variables from object that are not present in list 126 | _.forEach(obj, function (value, key) { 127 | !_.has(list.reference, key) && (delete obj[key]); 128 | }); 129 | 130 | // we first sync all variables in this list to the object 131 | list.each(function (variable) { 132 | obj[variable.key] = variable.valueOf(); 133 | }); 134 | 135 | return obj; 136 | }, 137 | 138 | /** 139 | * Fetches a variable and normalize its reference if disabled. 140 | * This updates the disabled variable `reference` in VariableList with its 141 | * last enabled duplicate(if found) in the `members` list. 142 | * 143 | * @private 144 | * @param {String} variableName - The name of the variable to get 145 | * @returns {Variable} - In case of duplicates, returns last enabled 146 | */ 147 | oneNormalizedVariable (variableName) { 148 | var indexKey = this._postman_listIndexKey, // `key` for Variable 149 | variable = this.reference[variableName], 150 | i; 151 | 152 | if (variable && !variable.disabled) { 153 | return variable; 154 | } 155 | 156 | // traverse the members list in reverse direction in order to find the last enabled 157 | for (i = this.members.length - 1; i >= 0; i--) { 158 | variable = this.members[i]; 159 | if (variable[indexKey] === variableName && !variable.disabled) { 160 | // update the input variable reference if comparison is not disabled 161 | this.reference[variableName] = variable; 162 | break; // return updated reference variable 163 | } 164 | } 165 | 166 | return this.reference[variableName]; 167 | } 168 | }); 169 | 170 | _.assign(VariableList, /** @lends VariableList */ { 171 | /** 172 | * Defines the name of this property for internal use. 173 | * 174 | * @private 175 | * @readOnly 176 | * @type {String} 177 | * 178 | * @note that this is directly accessed only in case of VariableList from _.findValue lodash util mixin 179 | */ 180 | _postman_propertyName: 'VariableList', 181 | 182 | /** 183 | * Checks whether an object is a VariableList 184 | * 185 | * @param {*} obj - 186 | * @returns {Boolean} 187 | */ 188 | isVariableList: function (obj) { 189 | return Boolean(obj) && ((obj instanceof VariableList) || 190 | _.inSuperChain(obj.constructor, '_postman_propertyName', VariableList._postman_propertyName)); 191 | } 192 | }); 193 | 194 | module.exports = { 195 | VariableList 196 | }; 197 | -------------------------------------------------------------------------------- /lib/collection/version.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | semver = require('semver'), 3 | PropertyBase = require('./property-base').PropertyBase, 4 | 5 | Version; 6 | 7 | /** 8 | * @typedef {Object|String} Version.definition 9 | */ 10 | _.inherit(( 11 | 12 | /** 13 | * Defines a Version. 14 | * 15 | * @constructor 16 | * @extends {PropertyBase} 17 | * @param {Version.definition} definition - 18 | */ 19 | Version = function PostmanPropertyVersion (definition) { 20 | // in case definition object is missing, there is no point moving forward 21 | if (!definition) { return; } 22 | 23 | // call the setter to process the version string and assign it to this object 24 | this.set(definition); 25 | }), PropertyBase); 26 | 27 | _.assign(Version.prototype, /** @lends Version.prototype */ { 28 | /** 29 | * Set the version value as string or object with separate components of version 30 | * 31 | * @draft 32 | * @param {object|string} value - 33 | */ 34 | set (value) { 35 | // extract the version logic and in case it failes and value passed is an object, we use that assuming parsed 36 | // value has been sent. 37 | var ver = semver.parse(value) || value || {}; 38 | 39 | _.assign(this, /** @lends Version.prototype */ { 40 | /** 41 | * The raw URL string. If {@link Version#set} is called with a string parameter, the string is saved here 42 | * before parsing various Version components. 43 | * 44 | * @type {String} 45 | */ 46 | raw: ver.raw, 47 | 48 | /** 49 | * @type {String} 50 | */ 51 | major: ver.major, 52 | 53 | /** 54 | * @type {String} 55 | */ 56 | minor: ver.minor, 57 | 58 | /** 59 | * @type {String} 60 | */ 61 | patch: ver.patch, 62 | 63 | /** 64 | * @type {String} 65 | */ 66 | prerelease: ver.prerelease && ver.prerelease.join && ver.prerelease.join() || ver.prerelease, 67 | 68 | /** 69 | * @type {String} 70 | */ 71 | build: ver.build && ver.build.join && ver.build.join() || ver.build, 72 | 73 | /** 74 | * @type {String} 75 | */ 76 | string: ver.version 77 | }); 78 | }, 79 | 80 | toString () { 81 | // TODO: is this enough? should we build the semver back up? 82 | return this.string || this.raw; 83 | } 84 | }); 85 | 86 | _.assign(Version, /** @lends Version */ { 87 | /** 88 | * Defines the name of this property for internal use. 89 | * 90 | * @private 91 | * @readOnly 92 | * @type {String} 93 | */ 94 | _postman_propertyName: 'Version' 95 | }); 96 | 97 | module.exports = { 98 | Version 99 | }; 100 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PropertyBase: require('./collection/property-base').PropertyBase, 3 | Certificate: require('./collection/certificate').Certificate, 4 | CertificateList: require('./collection/certificate-list').CertificateList, 5 | Collection: require('./collection/collection').Collection, 6 | Cookie: require('./collection/cookie').Cookie, 7 | CookieList: require('./collection/cookie-list').CookieList, 8 | Description: require('./collection/description').Description, 9 | Event: require('./collection/event').Event, 10 | EventList: require('./collection/event-list').EventList, 11 | FormParam: require('./collection/form-param').FormParam, 12 | Header: require('./collection/header').Header, 13 | HeaderList: require('./collection/header-list').HeaderList, 14 | Item: require('./collection/item').Item, 15 | ItemGroup: require('./collection/item-group').ItemGroup, 16 | MutationTracker: require('./collection/mutation-tracker').MutationTracker, 17 | PropertyList: require('./collection/property-list').PropertyList, 18 | Property: require('./collection/property').Property, 19 | QueryParam: require('./collection/query-param').QueryParam, 20 | Request: require('./collection/request').Request, 21 | RequestAuth: require('./collection/request-auth').RequestAuth, 22 | RequestBody: require('./collection/request-body').RequestBody, 23 | Response: require('./collection/response').Response, 24 | Script: require('./collection/script').Script, 25 | Url: require('./collection/url').Url, 26 | UrlMatchPattern: require('./url-pattern/url-match-pattern').UrlMatchPattern, 27 | UrlMatchPatternList: require('./url-pattern/url-match-pattern-list').UrlMatchPatternList, 28 | Variable: require('./collection/variable').Variable, 29 | VariableList: require('./collection/variable-list').VariableList, 30 | VariableScope: require('./collection/variable-scope').VariableScope, 31 | ProxyConfig: require('./collection/proxy-config').ProxyConfig, 32 | ProxyConfigList: require('./collection/proxy-config-list').ProxyConfigList, 33 | Version: require('./collection/version').Version 34 | }; 35 | -------------------------------------------------------------------------------- /lib/superstring/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | dynamicVariables = require('./dynamic-variables'), 3 | E = '', 4 | 5 | SuperString, // constructor 6 | Substitutor; // constructor 7 | 8 | /** 9 | * A string-like instance with additional functionalities to track string operations better 10 | * 11 | * @constructor 12 | * @private 13 | * @param {String} value - 14 | */ 15 | SuperString = function SuperString (value) { 16 | this.value = _.isString(value) ? value : (_.isFunction(value.toString) && value.toString() || E); 17 | 18 | /** 19 | * The total number of times there was a successful substitution. 20 | * 21 | * @type {number} 22 | */ 23 | this.substitutions = 0; 24 | 25 | /** 26 | * Keeps a track of the number of tokens replaced in the last replace command 27 | * 28 | * @type {number} 29 | */ 30 | this.replacements = 0; 31 | }; 32 | 33 | _.assign(SuperString.prototype, /** @lends SuperString.prototype */ { 34 | /** 35 | * Equivalent to string replace but performs additional tracking of the number of tokens replaced 36 | * 37 | * @param {RegExp|String} regex - 38 | * @param {Function|String} fn - 39 | * @returns {SuperString} 40 | */ 41 | replace (regex, fn) { 42 | var replacements = 0; // maintain a count of tokens replaced 43 | 44 | // to ensure we do not perform needless operations in the replacement, we use multiple replacement functions 45 | // after validating the parameters 46 | this.value = (this.value.replace(regex, _.isFunction(fn) ? 47 | function () { 48 | replacements += 1; 49 | 50 | return fn.apply(this, arguments); 51 | } : 52 | // this case is returned when replacer is not a function (ensures we do not need to check it) 53 | /* istanbul ignore next */ 54 | function () { 55 | replacements += 1; 56 | 57 | return fn; 58 | }) 59 | ); 60 | 61 | this.replacements = replacements; // store the last replacements 62 | replacements && (this.substitutions += 1); // if any replacement is done, count that some substitution was made 63 | 64 | return this; 65 | }, 66 | 67 | /** 68 | * @returns {String} 69 | */ 70 | toString () { 71 | return this.value; 72 | }, 73 | 74 | /** 75 | * @returns {String} 76 | */ 77 | valueOf () { 78 | return this.value; 79 | } 80 | }); 81 | 82 | /** 83 | * Perform replacement of tokens in a SuperString with values stored in keys of an array of objects. 84 | * 85 | * @constructor 86 | * @private 87 | * @param {Array} variables - 88 | * @param {Object} defaults - 89 | */ 90 | Substitutor = function (variables, defaults) { 91 | defaults && variables.push(defaults); 92 | this.variables = variables; 93 | }; 94 | 95 | _.assign(Substitutor.prototype, /** @lends Substitutor.prototype */ { 96 | /** 97 | * Find a key from the array of variable objects provided to the substitutor 98 | * 99 | * @param {String} key - 100 | * @returns {*} 101 | */ 102 | find (key) { 103 | var arr = this.variables, 104 | obj, 105 | value, 106 | i, 107 | ii; 108 | 109 | for (i = 0, ii = arr.length; i < ii; i++) { 110 | obj = arr[i]; 111 | // ensure that the item is an object 112 | if (!(obj && _.isObject(obj))) { 113 | continue; 114 | } 115 | 116 | // in case the object is a postman variable list, we give special attention 117 | if (obj.constructor._postman_propertyName === 'VariableList') { 118 | value = obj.oneNormalizedVariable(key); 119 | 120 | if (value && !value.disabled) { 121 | return value; 122 | } 123 | } 124 | // else we return the value from the plain object 125 | else if (_.has(obj, key)) { 126 | return obj[key]; 127 | } 128 | } 129 | }, 130 | 131 | /** 132 | * @param {String} value - 133 | * @returns {String} 134 | */ 135 | parse (value) { 136 | // convert the value into a SuperString so that it can return tracking results during replacements 137 | value = new SuperString(value); 138 | 139 | // get an instance of a replacer function that would be used to replace ejs like variable replacement 140 | // tokens 141 | var replacer = Substitutor.replacer(this); 142 | 143 | // replace the value once and keep on doing it until all tokens are replaced or we have reached a limit of 144 | // replacements 145 | do { 146 | value = value.replace(Substitutor.REGEX_EXTRACT_VARS, replacer); 147 | } while (value.replacements && (value.substitutions < Substitutor.VARS_SUBREPLACE_LIMIT)); 148 | 149 | // @todo: uncomment this code, and try to raise a warning in some way. 150 | // do a final check that if recursion limits are reached then replace with blank string 151 | // if (value.substitutions >= Substitutor.VARS_SUBREPLACE_LIMIT) { 152 | // value = value.replace(Substitutor.REGEX_EXTRACT_VARS, E); 153 | // } 154 | 155 | return value; 156 | } 157 | }); 158 | 159 | _.assign(Substitutor, /** @lends Substitutor */ { 160 | /** 161 | * Regular expression to be used in {String}.replace for extracting variable substitutions 162 | * 163 | * @readOnly 164 | * @type {RegExp} 165 | */ 166 | REGEX_EXTRACT_VARS: /{{([^{}]*?)}}/g, 167 | 168 | /** 169 | * Defines the number of times the variable substitution mechanism will repeat until all tokens are resolved 170 | * 171 | * @type {Number} 172 | */ 173 | VARS_SUBREPLACE_LIMIT: 19, 174 | 175 | /** 176 | * Maintain a list of types that are native 177 | * 178 | * @readOnly 179 | * @enum {String} 180 | */ 181 | NATIVETYPES: { 182 | string: true, 183 | number: true, 184 | boolean: true 185 | }, 186 | 187 | /** 188 | * Holds the default variables that Postman supports. 189 | * 190 | * @type {Object} 191 | */ 192 | DEFAULT_VARS: {}, 193 | 194 | /** 195 | * Create an instance of a substitutor or reuse one 196 | * 197 | * @param {Array|Substitutor} variables - 198 | * @param {Object=} defaults An object containing default variables to substitute 199 | * @returns {Substitutor} 200 | */ 201 | box: function (variables, defaults) { 202 | return (variables instanceof Substitutor) ? variables : new Substitutor(variables, defaults); 203 | }, 204 | 205 | /** 206 | * Checks whether a variable is instance of substitutor 207 | * 208 | * @param {*} subject - 209 | * @returns {Boolean} 210 | */ 211 | isInstance: function (subject) { 212 | return (subject instanceof Substitutor); 213 | }, 214 | 215 | /** 216 | * Get an instance of a function that is useful to be passed to a string replace function for extracting tokens 217 | * and replacing by substitutions 218 | * 219 | * @private 220 | * @param {Substitutor} substitutor - 221 | * @returns {Function} 222 | */ 223 | replacer: function (substitutor) { 224 | return function (match, token) { 225 | var r = substitutor.find(token); 226 | 227 | r && _.isFunction(r) && (r = r()); 228 | r && _.isFunction(r.toString) && (r = r.toString()); 229 | 230 | return Substitutor.NATIVETYPES[(typeof r)] ? r : match; 231 | }; 232 | } 233 | }); 234 | 235 | // @todo make the default variables of SuperString extensible and do this anywhere else but here 236 | _.forOwn(dynamicVariables, function (variable, name) { 237 | Substitutor.DEFAULT_VARS[name] = variable.generator; 238 | }); 239 | 240 | module.exports = { 241 | SuperString, 242 | Substitutor 243 | }; 244 | -------------------------------------------------------------------------------- /lib/url-pattern/url-match-pattern-list.js: -------------------------------------------------------------------------------- 1 | var _ = require('../util').lodash, 2 | PropertyList = require('../collection/property-list').PropertyList, 3 | Url = require('../collection/url').Url, 4 | UrlMatchPattern = require('./url-match-pattern').UrlMatchPattern, 5 | 6 | MATCH_ALL_URLS = UrlMatchPattern.MATCH_ALL_URLS, 7 | 8 | UrlMatchPatternList; 9 | 10 | _.inherit(( 11 | 12 | /** 13 | * UrlMatchPattern is a list of UrlMatchPatterns. 14 | * This allows you to test for any url over a list of match patterns. 15 | * 16 | * @constructor 17 | * @extends {PropertyList} 18 | * 19 | * @param {Object} parent - 20 | * @param {String[]} list - 21 | * @example An example UrlMatchPatternList 22 | * var matchPatternList = new UrlMatchPatternList(['https://*.google.com/*']); 23 | */ 24 | UrlMatchPatternList = function (parent, list) { 25 | UrlMatchPatternList.super_.call(this, UrlMatchPattern, parent, list); 26 | }), PropertyList); 27 | 28 | _.assign(UrlMatchPatternList.prototype, /** @lends UrlMatchPatternList.prototype */ { 29 | 30 | /** 31 | * Allows this property to be serialised into its plural form. 32 | * This is here because Property.prototype.toJSON() tries to singularise 33 | * the keys which are PropertyLists. 34 | * i.e. when a property has a key - `matches = new PropertyList()`, 35 | * toJSON on the property tries to singularise 'matches' and ends up with 'matche'. 36 | * 37 | * @private 38 | * @readOnly 39 | * @type {String} 40 | */ 41 | _postman_proprtyIsSerialisedAsPlural: true, 42 | 43 | /** 44 | * Tests the url string with the match pattern list provided to see if it matches any of it. 45 | * Follows the https://developer.chrome.com/extensions/match_patterns pattern for pattern validation and matching 46 | * 47 | * @param {String=} [urlStr] The url string for which the proxy match needs to be done. 48 | * @returns {Boolean=} 49 | */ 50 | test: function (urlStr) { 51 | /* 52 | * Similar to the UrlMatchPattern.test, however instead of testing 53 | * MATCH_ALL_URLS and Regex conditions serially with each of the pattern, 54 | * this method first searches for MATCH_ALL_URLS in all patterns 55 | * and then moves on to the slower Regex based searches. 56 | */ 57 | var url, 58 | matchAllUrlsPattern, 59 | matchedSpecificPattern; 60 | 61 | matchAllUrlsPattern = this.find(function (urlMatchPattern) { 62 | return urlMatchPattern.pattern === MATCH_ALL_URLS; 63 | }); 64 | 65 | if (_.isObject(matchAllUrlsPattern)) { 66 | return true; 67 | } 68 | 69 | url = new Url(urlStr); 70 | 71 | matchedSpecificPattern = this.find(function (urlMatchPattern) { 72 | var matchRegexObject = urlMatchPattern._matchPatternObject; 73 | 74 | // Empty matchRegexObject represents the match is INVALID match 75 | if (_.isEmpty(matchRegexObject)) { 76 | return false; 77 | } 78 | 79 | return (urlMatchPattern.testProtocol(url.protocol) && 80 | urlMatchPattern.testHost(url.getHost()) && 81 | urlMatchPattern.testPort(url.port, url.protocol) && 82 | urlMatchPattern.testPath(url.getPath())); 83 | }); 84 | 85 | return Boolean(matchedSpecificPattern); 86 | } 87 | }); 88 | 89 | _.assign(UrlMatchPatternList, /** @lends UrlMatchPatternList */ { 90 | /** 91 | * Defines the name of this property for internal use 92 | * 93 | * @private 94 | * @readOnly 95 | * @type {String} 96 | */ 97 | _postman_propertyName: 'UrlMatchPatternList' 98 | }); 99 | 100 | module.exports = { 101 | UrlMatchPatternList 102 | }; 103 | -------------------------------------------------------------------------------- /npm/build-docs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to generate documentation for this module. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const path = require('path'), 7 | 8 | chalk = require('chalk'), 9 | { exec, rm, test } = require('shelljs'), 10 | 11 | pkg = require('../package.json'), 12 | 13 | IS_WINDOWS = (/^win/).test(process.platform), 14 | TARGET_DIR = path.join('out', 'docs'); 15 | 16 | module.exports = function (exit) { 17 | console.info(chalk.yellow.bold('Generating documentation...')); 18 | 19 | try { 20 | // clean directory 21 | test('-d', TARGET_DIR) && rm('-rf', TARGET_DIR); 22 | } 23 | catch (e) { 24 | console.error(e.stack || e); 25 | 26 | return exit(e ? 1 : 0); 27 | } 28 | 29 | exec(`${IS_WINDOWS ? '' : 'node'} ${path.join('node_modules', '.bin', 'jsdoc')}${IS_WINDOWS ? '.cmd' : ''}` + 30 | ` -c .jsdoc-config.json -u docs lib --query 'pkgVersion=${pkg.version}'`, function (code) { 31 | // output status 32 | console.info(code ? 33 | chalk.red.bold('unable to genereate documentation') : 34 | ` - documentation created at "${TARGET_DIR}"`); 35 | exit(code); 36 | }); 37 | }; 38 | 39 | // ensure we run this script exports if this is a direct stdin.tty run 40 | !module.parent && module.exports(process.exit); 41 | -------------------------------------------------------------------------------- /npm/build-types.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to generate type-definition for this module. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const path = require('path'), 7 | fs = require('fs'), 8 | pkg = require('../package.json'), 9 | chalk = require('chalk'), 10 | { exec, rm, test } = require('shelljs'), 11 | 12 | IS_WINDOWS = (/^win/).test(process.platform), 13 | TARGET_DIR = path.join('types'), 14 | 15 | heading = 16 | `// Type definitions for postman-collection ${pkg.version} 17 | // Project: https://github.com/postmanlabs/postman-collection 18 | // Definitions by: PostmanLabs 19 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 20 | // TypeScript Version: 2.4 21 | /// 22 | `; 23 | 24 | module.exports = function (exit) { 25 | console.info(chalk.yellow.bold('Generating type-definitions...')); 26 | 27 | try { 28 | // clean directory 29 | test('-d', TARGET_DIR) && rm('-rf', TARGET_DIR); 30 | } 31 | catch (e) { 32 | console.error(e.stack || e); 33 | 34 | return exit(e ? 1 : 0); 35 | } 36 | 37 | exec(`${IS_WINDOWS ? '' : 'node'} ${path.join('node_modules', '.bin', 'jsdoc')}${IS_WINDOWS ? '.cmd' : ''}` + 38 | ' -c .jsdoc-config-type-def.json -p', function (code) { 39 | if (!code) { 40 | fs.readFile(`${TARGET_DIR}/index.d.ts`, function (err, contents) { 41 | if (err) { 42 | console.info(chalk.red.bold('unable to read the type-definition file')); 43 | exit(1); 44 | } 45 | var source = contents.toString(); 46 | 47 | source = source 48 | // replace all declare keyword with export, as the whole typedef will be wrapped around a module 49 | .replace(/^declare /gm, 'export ') 50 | // replacing String[] with string[] as 'String' is not a valid data-type in Typescript 51 | .replace(/String\[]/gm, 'string[]') 52 | // replacing Boolean[] with boolean[] as 'Boolean' is not a valid data-type in Typescript 53 | .replace(/Boolean\[]/gm, 'boolean[]') 54 | // removing all occurrences html, as the these tags are not supported in Type-definitions 55 | .replace(/<[^>]*>/gm, '') 56 | // replacing @link tags with the object namepath to which it was linked, 57 | // as these link tags are not navigable in type-definitions. 58 | .replace(/\{@link (\w*)[#.]+(\w*)\}/gm, '$1.$2') 59 | .replace(/\{@link (\S+)\}/gm, '$1') // remove @link tags 60 | .replace(/^(.+)/gm, ' $1'); 61 | 62 | source = `${heading}\ndeclare module "postman-collection" {\n\n${source}}\n`; 63 | 64 | fs.writeFile(`${TARGET_DIR}/index.d.ts`, source, function (err) { 65 | if (err) { 66 | console.info(chalk.red.bold('unable to write the type-definition file')); 67 | exit(1); 68 | } 69 | console.info(chalk.green.bold(`Type-definition file saved successfully at "${TARGET_DIR}"`)); 70 | exit(0); 71 | }); 72 | }); 73 | } 74 | else { 75 | // output status 76 | console.info(chalk.red.bold('unable to generate type-definition')); 77 | exit(code); 78 | } 79 | }); 80 | }; 81 | 82 | // ensure we run this script exports if this is a direct stdin.tty run 83 | !module.parent && module.exports(process.exit); 84 | -------------------------------------------------------------------------------- /npm/create-release.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to automate the versioning and changelog generation process for a release. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const shipit = require('@postman/shipit'), 7 | 8 | // npm run release [true] [beta] 9 | [pushToOrigin, preReleaseSuffix] = process.argv.splice(2); 10 | 11 | // only support `beta` suffix 12 | if (preReleaseSuffix && preReleaseSuffix !== 'beta') { 13 | throw new Error(`Can't prerelease with \`${preReleaseSuffix}\` suffix.`); 14 | } 15 | 16 | // 🚢 Just Ship It! 17 | shipit({ 18 | mainBranch: 'main', 19 | // don't push to origin unless explicitly set 20 | pushToOrigin: pushToOrigin === 'true', 21 | // prerelease suffix, if any 22 | preReleaseSuffix: preReleaseSuffix, 23 | // make sure that following dependencies are up to date 24 | dependencyList: [ 25 | 'http-reasons', 'liquid-json', 'mime-format', 'postman-url-encoder' 26 | ] 27 | }).then((version) => { 28 | console.info('🚀', version); 29 | }).catch((err) => { 30 | console.error('🔥', err); 31 | process.exit(1); 32 | }); 33 | -------------------------------------------------------------------------------- /npm/publish-coverage.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* eslint-disable require-jsdoc */ 3 | // --------------------------------------------------------------------------------------------------------------------- 4 | // This script is intended to publish coverage reports to Codecov. 5 | // 6 | // It verifies the Codecov's bash uploader checksum before execution. 7 | // Usage: npm run codecov -- 8 | // Refer: https://about.codecov.io/security-update/ 9 | // --------------------------------------------------------------------------------------------------------------------- 10 | 11 | const https = require('https'), 12 | crypto = require('crypto'), 13 | promisify = require('util').promisify, 14 | 15 | // eslint-disable-next-line security/detect-child-process 16 | exec = promisify(require('child_process').exec), 17 | writeFile = promisify(require('fs').writeFile), 18 | 19 | chalk = require('chalk'), 20 | 21 | CODECOV_PATH = '.coverage/codecov.sh', 22 | BASH_UPLOADER_URL = 'https://codecov.io/bash', 23 | BASH_UPLOADER_BASE = 'https://raw.githubusercontent.com/codecov/codecov-bash'; 24 | 25 | function wget (url) { 26 | return new Promise((resolve, reject) => { 27 | const req = https.request(url, (res) => { 28 | if (res.statusCode !== 200) { 29 | return reject(new Error('non-200 response')); 30 | } 31 | 32 | let data = ''; 33 | 34 | res.on('data', (chunk) => { 35 | data += chunk; 36 | }); 37 | 38 | res.on('end', () => { 39 | resolve(data); 40 | }); 41 | }); 42 | 43 | req.on('error', (err) => { 44 | reject(err); 45 | }); 46 | 47 | req.end(); 48 | }); 49 | } 50 | 51 | function getVersion (script) { 52 | const match = script.match(/VERSION="([0-9.]*)"/); 53 | 54 | return match ? match[1] : null; 55 | } 56 | 57 | async function getPublicChecksum (version, encryption) { 58 | const url = `${BASH_UPLOADER_BASE}/${version}/SHA${encryption}SUM`, 59 | checksumResponse = await wget(url); 60 | 61 | // return codecov checksum only 62 | return checksumResponse.split('\n')[0]; 63 | } 64 | 65 | function calculateChecksum (script, encryption) { 66 | const shasum = crypto.createHash(`sha${encryption}`); 67 | 68 | shasum.update(script); 69 | 70 | return `${shasum.digest('hex')} codecov`; 71 | } 72 | 73 | async function validateScript (script) { 74 | const version = getVersion(script); 75 | 76 | if (!version) { 77 | throw new Error('Missing bash uploader version'); 78 | } 79 | 80 | for (const encryption of [1, 256, 512]) { 81 | // eslint-disable-next-line no-await-in-loop 82 | const publicChecksum = await getPublicChecksum(version, encryption), 83 | uploaderChecksum = calculateChecksum(script, encryption); 84 | 85 | if (uploaderChecksum !== publicChecksum) { 86 | throw new Error(`SHA${encryption} checksum mismatch`); 87 | } 88 | } 89 | } 90 | 91 | module.exports = async function () { 92 | // banner line 93 | console.info(chalk.yellow.bold('Publishing coverage reports...')); 94 | 95 | const args = process.argv.slice(2), 96 | script = await wget(BASH_UPLOADER_URL); 97 | 98 | await validateScript(script); 99 | await writeFile(CODECOV_PATH, script); 100 | 101 | return exec(`bash ${CODECOV_PATH} ${args.join(' ')}`); 102 | }; 103 | 104 | // ensure we run this script exports if this is a direct stdin.tty run 105 | if (!module.parent) { 106 | module.exports() 107 | .then(({ stdout, stderr }) => { 108 | console.info(stdout); 109 | console.info(stderr); 110 | }) 111 | .catch(({ message, stack, stdout, stderr }) => { 112 | console.error(stack || message); 113 | stdout && console.info(stdout); 114 | stderr && console.info(stderr); 115 | 116 | process.exit(1); 117 | }); 118 | } 119 | -------------------------------------------------------------------------------- /npm/test-benchmark.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to execute all benchmark tests. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const chalk = require('chalk'), 7 | { exec } = require('shelljs'); 8 | 9 | module.exports = function (exit) { 10 | console.info(chalk.yellow.bold('Running benchmark tests')); 11 | exec('bipbip test/benchmark/ ' + 12 | '--save benchmark/benchmark-results.json ' + 13 | '--compare benchmark/benchmark-results.json', exit); 14 | }; 15 | 16 | // ensure we run this script exports if this is a direct stdin.tty run 17 | !module.parent && module.exports(process.exit); 18 | -------------------------------------------------------------------------------- /npm/test-browser.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to execute all unit tests in the Chrome Browser. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const path = require('path'), 7 | 8 | chalk = require('chalk'), 9 | KarmaServer = require('karma').Server, 10 | 11 | KARMA_CONFIG_PATH = path.join(__dirname, '..', 'test', 'karma.conf'); 12 | 13 | module.exports = function (exit) { 14 | console.info(chalk.yellow.bold('Running unit tests within browser...')); 15 | 16 | (new KarmaServer({ // eslint-disable no-new 17 | cmd: 'start', 18 | configFile: KARMA_CONFIG_PATH 19 | }, exit)).start(); 20 | }; 21 | 22 | // ensure we run this script exports if this is a direct stdin.tty run 23 | !module.parent && module.exports(process.exit); 24 | -------------------------------------------------------------------------------- /npm/test-lint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to contain all actions pertaining to code style checking, linting and normalization. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const chalk = require('chalk'), 7 | { ESLint } = require('eslint'), 8 | 9 | LINT_SOURCE_DIRS = [ 10 | './test/**/*.js', 11 | './index.js', 12 | './lib/**/*.js', 13 | './npm/**/*.js' 14 | ]; 15 | 16 | module.exports = async function (exit) { 17 | // banner line 18 | console.info(chalk.yellow.bold('\nLinting files using eslint...')); 19 | 20 | const eslint = new ESLint(), 21 | results = await eslint.lintFiles(LINT_SOURCE_DIRS), 22 | errorReport = ESLint.getErrorResults(results), 23 | formatter = await eslint.loadFormatter(); 24 | 25 | // log the result to CLI 26 | console.info(formatter.format(results)); 27 | 28 | (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); 29 | 30 | exit(Number(errorReport && errorReport.length) || 0); 31 | }; 32 | 33 | // ensure we run this script exports if this is a direct stdin.tty run 34 | !module.parent && module.exports(process.exit); 35 | -------------------------------------------------------------------------------- /npm/test-system.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to execute all system tests. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const path = require('path'), 7 | 8 | Mocha = require('mocha'), 9 | chalk = require('chalk'), 10 | recursive = require('recursive-readdir'), 11 | { exec } = require('shelljs'), 12 | 13 | SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'system'); 14 | 15 | module.exports = function (exit) { 16 | // banner line 17 | console.info(chalk.yellow.bold('\nRunning system tests using mocha...')); 18 | 19 | // add all spec files to mocha 20 | recursive(SPEC_SOURCE_DIR, (err, files) => { 21 | if (err) { 22 | console.error(err); 23 | 24 | return exit(1); 25 | } 26 | 27 | const mocha = new Mocha({ timeout: 1000 * 60 }); 28 | 29 | files.filter((file) => { // extract all test files 30 | return (file.substr(-8) === '.test.js'); 31 | }).forEach(mocha.addFile.bind(mocha)); 32 | 33 | // start the mocha run 34 | mocha.run((runError) => { 35 | if (runError) { 36 | console.error(runError.stack || runError); 37 | 38 | return exit(1); 39 | } 40 | 41 | // ensure all dependencies are okay 42 | console.info(chalk.yellow('checking package dependencies...\n')); 43 | exec('dependency-check ./package.json --extra --no-dev --missing', (code) => { 44 | exit(code ? 1 : 0); 45 | }); 46 | }); 47 | }); 48 | }; 49 | 50 | // ensure we run this script exports if this is a direct stdin.tty run 51 | !module.parent && module.exports(process.exit); 52 | -------------------------------------------------------------------------------- /npm/test-unit.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // This script is intended to execute all unit tests. 4 | // --------------------------------------------------------------------------------------------------------------------- 5 | 6 | const path = require('path'), 7 | 8 | chalk = require('chalk'), 9 | Mocha = require('mocha'), 10 | recursive = require('recursive-readdir'), 11 | 12 | SPEC_SOURCE_DIR = path.join('test', 'unit'); 13 | 14 | module.exports = function (exit) { 15 | // banner line 16 | console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); 17 | 18 | // add all spec files to mocha 19 | recursive(SPEC_SOURCE_DIR, (err, files) => { 20 | if (err) { 21 | console.error(err); 22 | 23 | return exit(1); 24 | } 25 | 26 | const mocha = new Mocha({ timeout: 1000 * 60 }); 27 | 28 | files.filter((file) => { // extract all test files 29 | return (file.substr(-8) === '.test.js'); 30 | }).forEach(mocha.addFile.bind(mocha)); 31 | 32 | // start the mocha run 33 | mocha.run((runError) => { 34 | runError && console.error(runError.stack || runError); 35 | 36 | exit(runError || process.exitCode ? 1 : 0); 37 | }); 38 | }); 39 | }; 40 | 41 | // ensure we run this script exports if this is a direct stdin.tty run 42 | !module.parent && module.exports(process.exit); 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postman-collection", 3 | "version": "5.0.2", 4 | "description": "Enables developers to use a unified Postman Collection format Object across projects", 5 | "author": "Postman Inc.", 6 | "license": "Apache-2.0", 7 | "main": "index.js", 8 | "homepage": "https://github.com/postmanlabs/postman-collection#readme", 9 | "bugs": { 10 | "url": "https://github.com/postmanlabs/postman-collection/issues", 11 | "email": "help@postman.com" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/postmanlabs/postman-collection.git" 16 | }, 17 | "keywords": [ 18 | "postman", 19 | "collection", 20 | "sdk" 21 | ], 22 | "scripts": { 23 | "build-docs": "node npm/build-docs.js", 24 | "build-types": "node npm/build-types.js", 25 | "codecov": "node npm/publish-coverage.js", 26 | "release": "node npm/create-release.js", 27 | "test": "npm run test-lint && npm run test-system && npm run test-unit && npm run test-browser", 28 | "test-benchmark": "node npm/test-benchmark.js", 29 | "test-browser": "node npm/test-browser.js", 30 | "test-lint": "node npm/test-lint.js", 31 | "test-system": "node npm/test-system.js", 32 | "test-unit": "nyc --nycrc-path=.nycrc.js node npm/test-unit.js" 33 | }, 34 | "dependencies": { 35 | "@faker-js/faker": "5.5.3", 36 | "file-type": "3.9.0", 37 | "http-reasons": "0.1.0", 38 | "iconv-lite": "0.6.3", 39 | "liquid-json": "0.3.1", 40 | "lodash": "4.17.21", 41 | "mime-format": "2.0.2", 42 | "mime-types": "2.1.35", 43 | "postman-url-encoder": "3.0.7", 44 | "semver": "7.7.1", 45 | "uuid": "8.3.2" 46 | }, 47 | "devDependencies": { 48 | "@postman/shipit": "^0.4.0", 49 | "@stylistic/eslint-plugin-js": "^1.8.0", 50 | "async": "^3.2.6", 51 | "bipbip": "^0.4.2", 52 | "browserify": "^17.0.1", 53 | "btoa": "^1.2.1", 54 | "chai": "^4.5.0", 55 | "chalk": "^4.1.2", 56 | "dependency-check": "^4.1.0", 57 | "eslint": "^8.57.0", 58 | "eslint-plugin-jsdoc": "^47.0.2", 59 | "eslint-plugin-lodash": "^7.4.0", 60 | "eslint-plugin-mocha": "^10.5.0", 61 | "eslint-plugin-n": "^16.6.2", 62 | "eslint-plugin-security": "^2.1.1", 63 | "js-yaml": "^4.1.0", 64 | "jsdoc": "^3.6.11", 65 | "karma": "^6.4.4", 66 | "karma-browserify": "^8.1.0", 67 | "karma-chrome-launcher": "^3.2.0", 68 | "karma-mocha": "^2.0.1", 69 | "karma-mocha-reporter": "^2.2.5", 70 | "mocha": "^11.1.0", 71 | "nyc": "^17.1.0", 72 | "parse-gitignore": "^2.0.0", 73 | "postman-jsdoc-theme": "^0.0.3", 74 | "postman-request": "^2.88.1-postman.31", 75 | "recursive-readdir": "^2.2.3", 76 | "require-all": "^3.0.0", 77 | "shelljs": "^0.8.5", 78 | "strip-json-comments": "^3.1.1", 79 | "tsd-jsdoc": "^2.5.0" 80 | }, 81 | "engines": { 82 | "node": ">=18" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "mocha" 4 | ], 5 | "env": { 6 | "mocha": true, 7 | "browser": true, 8 | "node": true, 9 | "es6": true 10 | }, 11 | "globals": { 12 | "afterEach": true, 13 | "beforeEach": true, 14 | "describe": true, 15 | "expect": true, 16 | "it": true, 17 | "scenario": true 18 | }, 19 | "rules": { 20 | // Mocha 21 | "mocha/handle-done-callback": "error", 22 | "mocha/max-top-level-suites": "error", 23 | "mocha/no-exclusive-tests": "error", 24 | "mocha/no-global-tests": "error", 25 | "mocha/no-hooks-for-single-case": "off", 26 | "mocha/no-hooks": "off", 27 | "mocha/no-identical-title": "error", 28 | "mocha/no-mocha-arrows": "error", 29 | "mocha/no-nested-tests": "error", 30 | "mocha/no-pending-tests": "error", 31 | "mocha/no-return-and-callback": "error", 32 | "mocha/no-sibling-hooks": "error", 33 | "mocha/no-skipped-tests": "warn", 34 | "mocha/no-synchronous-tests": "off", 35 | "mocha/no-top-level-hooks": "warn", 36 | "mocha/valid-test-description": "off", 37 | "mocha/valid-suite-description": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/benchmark/variable-substitution.bench.js: -------------------------------------------------------------------------------- 1 | suite('SuperString variable substitution', function () { 2 | var SDK = require('../../'), 3 | fakerMap = require('../../lib/superstring/faker-map'), 4 | 5 | manyVarsFixture4 = Object.keys(fakerMap).filter((item, index) => { return index < 4; }).map((varname) => { 6 | return '{{$random' + varname + '}}'; 7 | }).join(' '), 8 | 9 | manyVarsFixture8 = Object.keys(fakerMap).filter((item, index) => { return index < 8; }).map((varname) => { 10 | return '{{$random' + varname + '}}'; 11 | }).join(' '), 12 | 13 | manyVarsFixture16 = Object.keys(fakerMap).filter((item, index) => { return index < 16; }).map((varname) => { 14 | return '{{$random' + varname + '}}'; 15 | }).join(' '), 16 | 17 | manyVarsFixture32 = Object.keys(fakerMap).filter((item, index) => { return index < 32; }).map((varname) => { 18 | return '{{$random' + varname + '}}'; 19 | }).join(' '), 20 | 21 | manyVarsFixture64 = Object.keys(fakerMap).filter((item, index) => { return index < 64; }).map((varname) => { 22 | return '{{$random' + varname + '}}'; 23 | }).join(' '); 24 | 25 | scenario('single constant dynamic variable', () => { 26 | SDK.Property.replaceSubstitutions('{{$randomName}}'); 27 | }); 28 | 29 | scenario('two constant dynamic variables', () => { 30 | SDK.Property.replaceSubstitutions('{{$randomName}} has transferred {{$randomBitcoin}} bitcoin'); 31 | }); 32 | 33 | scenario('four constant dynamic variables', () => { 34 | // eslint-disable-next-line @stylistic/js/max-len 35 | SDK.Property.replaceSubstitutions(manyVarsFixture4); 36 | }); 37 | 38 | scenario('eight constant dynamic variables', () => { 39 | // eslint-disable-next-line @stylistic/js/max-len 40 | SDK.Property.replaceSubstitutions(manyVarsFixture8); 41 | }); 42 | 43 | scenario('sixteen constant dynamic variables', () => { 44 | SDK.Property.replaceSubstitutions(manyVarsFixture16); 45 | }); 46 | 47 | scenario('thirty-two constant dynamic variables', () => { 48 | SDK.Property.replaceSubstitutions(manyVarsFixture32); 49 | }); 50 | 51 | scenario('sixty-four constant dynamic variables', () => { 52 | SDK.Property.replaceSubstitutions(manyVarsFixture64); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/fixtures/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postmanlabs/postman-collection/7a114de2841fbe8c46ad90147694b4435c5e102c/test/fixtures/audio.mp3 -------------------------------------------------------------------------------- /test/fixtures/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postmanlabs/postman-collection/7a114de2841fbe8c46ad90147694b4435c5e102c/test/fixtures/icon.png -------------------------------------------------------------------------------- /test/fixtures/japaneseCharacters.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postmanlabs/postman-collection/7a114de2841fbe8c46ad90147694b4435c5e102c/test/fixtures/japaneseCharacters.txt -------------------------------------------------------------------------------- /test/fixtures/russianCharacters.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postmanlabs/postman-collection/7a114de2841fbe8c46ad90147694b4435c5e102c/test/fixtures/russianCharacters.txt -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | module.exports = function (config) { 3 | var configuration = { 4 | 5 | // base path that will be used to resolve all patterns (eg. files, exclude) 6 | basePath: '', 7 | 8 | // frameworks to use 9 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['mocha', 'browserify'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: [ 14 | '../index.js', 15 | '../test/unit/**/*.js' 16 | ], 17 | 18 | // preprocess matching files before serving them to the browser 19 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 20 | preprocessors: { 21 | '../index.js': ['browserify'], // Mention path as per your test js folder 22 | '../test/unit/**/*.js': ['browserify'] // Mention path as per your library js folder 23 | }, 24 | // test results reporter to use 25 | // possible values: 'dots', 'progress' 26 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 27 | reporters: ['mocha'], 28 | 29 | // web server port 30 | port: 9876, 31 | 32 | // enable / disable colors in the output (reporters and logs) 33 | colors: true, 34 | 35 | // level of logging 36 | // one of: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 37 | logLevel: config.LOG_WARN, 38 | 39 | // enable / disable watching file and executing tests whenever any file changes 40 | autoWatch: false, 41 | 42 | // start these browsers 43 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 44 | browsers: ['ChromeHeadless'], 45 | 46 | // Continuous Integration mode 47 | // if true, Karma captures browsers, runs the tests and exits 48 | singleRun: true, 49 | 50 | // Concurrency level 51 | // how many browser should be started simultaneously 52 | concurrency: Infinity, 53 | 54 | // Uncomment "karma-browserify" if you see an error like this: 55 | // Error: No provider for "framework:browserify"! (Resolving: framework:browserify) 56 | plugins: [ 57 | 'karma-mocha', 58 | 'karma-chrome-launcher', 59 | 'karma-browserify', 60 | 'karma-mocha-reporter' 61 | ], 62 | 63 | // Pass options to the client frameworks. 64 | client: { 65 | mocha: { 66 | timeout: 10000 // 10 seconds 67 | } 68 | } 69 | }; 70 | 71 | // Use `npm run test-browser -- --debug` to debug tests in Chrome console 72 | if (process.argv[2] === '--debug') { 73 | configuration.browsers = ['Chrome']; 74 | configuration.singleRun = false; 75 | } 76 | 77 | config.set(configuration); 78 | }; 79 | -------------------------------------------------------------------------------- /test/system/jsdoc-config.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview This test specs runs tests on the ,jsdoc-config.json file of repository. It has a set of strict tests 3 | * on the content of the file as well. Any change to .jsdoc-config.json must be accompanied by valid test case in this 4 | * spec-sheet. 5 | */ 6 | const fs = require('fs'), 7 | expect = require('chai').expect; 8 | 9 | describe('JSDoc configuration', function () { 10 | var json, 11 | content, 12 | jsdocConfigPath = './.jsdoc-config.json'; 13 | 14 | it('should exist', function (done) { 15 | fs.stat(jsdocConfigPath, done); 16 | }); 17 | 18 | it('should have readable content', function () { 19 | expect(content = fs.readFileSync(jsdocConfigPath).toString(), 'Should have readable content').to.be.ok; 20 | }); 21 | 22 | it('should have valid JSON content', function () { 23 | expect(json = JSON.parse(content), 'Should have valid JSON content').to.be.ok; 24 | }); 25 | 26 | describe('tags', function () { 27 | it('should allow unknown tags', function () { 28 | expect(json.tags.allowUnknownTags, 'Should allow unknown tags').to.be.ok; 29 | }); 30 | 31 | it('should have jsdoc and closure dictionaries', function () { 32 | expect(json.tags.dictionaries).to.eql(['jsdoc', 'closure']); 33 | }); 34 | }); 35 | 36 | describe('source', function () { 37 | it('should have an include pattern', function () { 38 | expect(json.source.includePattern).to.equal('.+\\.js(doc)?$'); 39 | }); 40 | 41 | it('should have an exclude pattern', function () { 42 | expect(json.source.excludePattern).to.equal('(^|\\/|\\\\)_'); 43 | }); 44 | }); 45 | 46 | describe('plugins', function () { 47 | it('should have the markdown plugin', function () { 48 | expect(json.plugins, 'Should use the markdown plugin').to.include('plugins/markdown'); 49 | }); 50 | }); 51 | 52 | describe('templates', function () { 53 | it('should not have clever links', function () { 54 | expect(json.templates.cleverLinks).to.be.false; 55 | }); 56 | 57 | it('should not have monospace links', function () { 58 | expect(json.templates.monospaceLinks).to.be.false; 59 | }); 60 | 61 | it('should highlight tutorial code', function () { 62 | expect(json.templates.highlightTutorialCode).to.be.ok; 63 | }); 64 | }); 65 | 66 | describe('opts', function () { 67 | it('should use the Postman JSDoc theme', function () { 68 | expect(json.opts.template).to.equal('./node_modules/postman-jsdoc-theme'); 69 | }); 70 | 71 | it('should use UTF-8 encoding', function () { 72 | expect(json.opts.encoding).to.equal('utf8'); 73 | }); 74 | 75 | it('should create documentation in out/docs', function () { 76 | expect(json.opts.destination).to.equal('./out/docs'); 77 | }); 78 | 79 | it('should recurse', function () { 80 | expect(json.opts.recurse).to.be.ok; 81 | }); 82 | 83 | it('should have a valid readme', function () { 84 | expect(json.opts.readme, 'Should use a valid readme').to.equal('README.md'); 85 | }); 86 | }); 87 | 88 | describe('markdown', function () { 89 | it('should have a gfm parser', function () { 90 | expect(json.markdown.parser, 'Should use the gfm markdown parser').to.equal('gfm'); 91 | }); 92 | 93 | it('should have jsdoc and closure dictionaries', function () { 94 | expect(json.markdown.hardwrap).to.be.false; 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/system/npm-publish.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect, 2 | // eslint-disable-next-line security/detect-child-process 3 | exec = require('child_process').execSync; 4 | 5 | describe('npm publish', function () { 6 | const packageInfo = JSON.parse(exec('npm pack --dry-run --json'))[0], 7 | packagedFiles = packageInfo.files.map(({ path }) => { return path; }); 8 | 9 | it('should have a valid package name', function () { 10 | expect(packageInfo.name).to.equal('postman-collection'); 11 | }); 12 | 13 | it('should not publish unnecessary files', function () { 14 | const allowedFiles = ['index.js', 'package.json', 'LICENSE.md', 'README.md', 'CHANGELOG.yaml']; 15 | 16 | packagedFiles.forEach(function (path) { 17 | expect(allowedFiles.includes(path) || 18 | path.startsWith('lib/') || 19 | path.startsWith('types/')).to.be.true; 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/system/repository.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview This test specs runs tests on the package.json file of repository. It has a set of strict tests on the 3 | * content of the file as well. Any change to package.json must be accompanied by valid test case in this spec-sheet. 4 | */ 5 | const _ = require('lodash'), 6 | yml = require('js-yaml'), 7 | expect = require('chai').expect, 8 | parseIgnore = require('parse-gitignore'); 9 | 10 | describe('project repository', function () { 11 | var fs = require('fs'); 12 | 13 | describe('package.json', function () { 14 | var content, 15 | json; 16 | 17 | it('should exist', function (done) { 18 | fs.stat('./package.json', done); 19 | }); 20 | 21 | it('should have readable JSON content', function () { 22 | expect(content = fs.readFileSync('./package.json').toString(), 'Should have readable content').to.be.ok; 23 | }); 24 | 25 | it('should have valid JSON content', function () { 26 | expect(json = JSON.parse(content), 'Should have valid JSON content').to.be.ok; 27 | }); 28 | 29 | describe('package.json JSON data', function () { 30 | it('should have valid name, description, author and license', function () { 31 | expect(json).to.include.keys({ 32 | name: 'postman-collection', 33 | description: 'Enables developers to use a unified Postman Collection format Object across projects', 34 | author: 'Postman Labs ', 35 | license: 'Apache-2.0' 36 | }); 37 | }); 38 | 39 | it('should have a valid version string in form of ..', function () { 40 | expect(json.version) 41 | // eslint-disable-next-line @stylistic/js/max-len, security/detect-unsafe-regex 42 | .to.match(/^((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$/); 43 | }); 44 | }); 45 | 46 | describe('devDependencies', function () { 47 | it('should exist', function () { 48 | expect(json.devDependencies).to.be.an('object'); 49 | }); 50 | 51 | it('should point to a valid semver', function () { 52 | Object.keys(json.devDependencies).forEach(function (dependencyName) { 53 | // eslint-disable-next-line security/detect-non-literal-regexp 54 | expect(json.devDependencies[dependencyName]).to.match(new RegExp('((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + 55 | '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); 56 | }); 57 | }); 58 | }); 59 | 60 | describe('dependencies', function () { 61 | it('should exist', function () { 62 | expect(json.dependencies).to.be.an('object'); 63 | }); 64 | 65 | it('should point to specific package version; (*, ^, ~) not expected', function () { 66 | _.forEach(json.dependencies, function (dep) { 67 | expect((/^\d/).test(dep)).to.be.ok; 68 | }); 69 | }); 70 | 71 | // @note file-type v4 was not browserify compatible and the latest 72 | // version is async, we don't use it in a critical flow and since it 73 | // does not have any dependencies, it's safe to use this older version. 74 | it('should have dependency file-type v3.9.0', function () { 75 | expect(json.dependencies).to.have.property('file-type', '3.9.0'); 76 | }); 77 | }); 78 | 79 | describe('main entry script', function () { 80 | it('should point to a valid file', function (done) { 81 | expect(json.main).to.equal('index.js'); 82 | fs.stat(json.main, done); 83 | }); 84 | }); 85 | }); 86 | 87 | describe('README.md', function () { 88 | it('should exist', function (done) { 89 | fs.stat('./README.md', done); 90 | }); 91 | 92 | it('should have readable content', function () { 93 | expect(fs.readFileSync('./README.md').toString()).to.be.ok; 94 | }); 95 | }); 96 | 97 | describe('LICENSE.md', function () { 98 | it('should exist', function (done) { 99 | fs.stat('./LICENSE.md', done); 100 | }); 101 | 102 | it('should have readable content', function () { 103 | expect(fs.readFileSync('./LICENSE.md').toString()).to.be.ok; 104 | }); 105 | }); 106 | 107 | describe('.gitattributes', function () { 108 | it('should exist', function (done) { 109 | fs.stat('./.gitattributes', done); 110 | }); 111 | 112 | it('should have readable content', function () { 113 | expect(fs.readFileSync('./.gitattributes').toString()).to.be.ok; 114 | }); 115 | }); 116 | 117 | describe('CHANGELOG.yaml', function () { 118 | it('should exist', function (done) { 119 | fs.stat('./CHANGELOG.yaml', done); 120 | }); 121 | 122 | it('should have readable content', function () { 123 | expect(yml.load(fs.readFileSync('./CHANGELOG.yaml')), 'not a valid yaml').to.be.ok; 124 | }); 125 | }); 126 | 127 | describe('.ignore files', function () { 128 | var gitignorePath = '.gitignore', 129 | npmignorePath = '.npmignore', 130 | npmignore = parseIgnore(fs.readFileSync(npmignorePath)).patterns, 131 | gitignore = parseIgnore(fs.readFileSync(gitignorePath)).patterns; 132 | 133 | describe(gitignorePath, function () { 134 | it('should exist', function (done) { 135 | fs.stat(gitignorePath, done); 136 | }); 137 | 138 | it('should have valid content', function () { 139 | expect(gitignore).to.be.an('array'); 140 | }); 141 | }); 142 | 143 | describe(npmignorePath, function () { 144 | it('should exist', function (done) { 145 | fs.stat(npmignorePath, done); 146 | }); 147 | 148 | it('should have valid content', function () { 149 | expect(npmignore).to.be.an('array'); 150 | }); 151 | }); 152 | 153 | it('should have .gitignore coverage to be a subset of .npmignore coverage', function () { 154 | expect(_.intersection(gitignore, npmignore)).to.eql(gitignore); 155 | }); 156 | }); 157 | 158 | describe('.eslintrc', function () { 159 | var stripJSON = require('strip-json-comments'), 160 | 161 | content, 162 | json; 163 | 164 | it('should exist', function (done) { 165 | fs.stat('./.eslintrc', done); 166 | }); 167 | 168 | it('should have readable content', function () { 169 | expect(content = fs.readFileSync('./.eslintrc').toString()).to.be.ok; 170 | }); 171 | 172 | it('should be valid JSON content', function () { 173 | expect(json = JSON.parse(stripJSON(content))).to.be.ok; 174 | }); 175 | 176 | it('should have appropriate plugins specified', function () { 177 | expect(json.plugins).to.eql(['n', 'jsdoc', 'lodash', 'security', '@stylistic/js']); 178 | }); 179 | 180 | it('should have appropriate environments specified', function () { 181 | expect(json.env.browser).to.be.true; 182 | expect(json.env.node).to.be.true; 183 | }); 184 | }); 185 | }); 186 | -------------------------------------------------------------------------------- /test/system/source-modules.test.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | _ = require('lodash'), 3 | sdk = require('../../lib'), 4 | expect = require('chai').expect, 5 | 6 | BASELESS_MODULES = ['Description']; 7 | 8 | describe('collection module', function () { 9 | var modules = require('require-all')({ 10 | dirname: path.join(__dirname, '/../../lib/collection'), 11 | excludeDirs: /^\.(git|svn)$/, 12 | recursive: true 13 | }); 14 | 15 | modules = _.mapValues(modules, function (value, key) { 16 | var name = (key.substr(0, 1).toUpperCase() + key.substr(1)).replace(/-([a-z])/g, function (g) { 17 | return g[1].toUpperCase(); 18 | }); 19 | 20 | return { 21 | module: value, 22 | property: value[name], 23 | file: key, 24 | name: name 25 | }; 26 | }); 27 | 28 | Object.keys(modules).forEach(function (file) { 29 | describe(modules[file].name, function () { 30 | var meta = modules[file], 31 | Module = meta.property; 32 | 33 | it('should be exported in the SDK', function () { 34 | expect(sdk[meta.name]).to.be.ok; 35 | }); 36 | 37 | it('should have its name defined in the constructor', function () { 38 | expect(Module._postman_propertyName).to.equal(meta.name); 39 | }); 40 | 41 | it('should be constructed with no parameter', function () { 42 | var err; 43 | 44 | try { 45 | // eslint-disable-next-line no-new 46 | new Module(); 47 | } 48 | catch (e) { 49 | err = e; 50 | } 51 | 52 | expect(err).to.be.undefined; 53 | }); 54 | 55 | !_.includes(BASELESS_MODULES, meta.name) && it('should inherit from PropertyBase', function () { 56 | expect((new Module()) instanceof modules['property-base'].property).to.be.ok; 57 | }); 58 | 59 | _.includes(BASELESS_MODULES, meta.name) && it('should not inherit from PropertyBase', function () { 60 | expect((new Module()) instanceof modules['property-base'].property).to.be.false; 61 | }); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/unit/certificate-list.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | CertificateList = require('../../lib/index.js').CertificateList, 3 | Certificate = require('../../lib/index.js').Certificate; 4 | 5 | describe('CertificateList', function () { 6 | describe('resolve', function () { 7 | var certificateList = new CertificateList({}, [{ 8 | id: 1, 9 | 10 | matches: ['https://*.google.com/*', 'https://*.youtube.com'] 11 | }, { 12 | id: 2, 13 | 14 | matches: ['https://example.com', 'https://*.foo.com'] 15 | }]); 16 | 17 | it('should return a certificate instance when matched, and undefined instead', function () { 18 | var matchedCertificate = certificateList.resolveOne('https://www.google.com'); 19 | 20 | expect(matchedCertificate).to.be.an.instanceof(Certificate); 21 | 22 | matchedCertificate = certificateList.resolveOne('https://www.google.com:443'); 23 | expect(matchedCertificate).to.be.an.instanceof(Certificate); 24 | 25 | matchedCertificate = certificateList.resolveOne('https://www.bla.com'); 26 | expect(matchedCertificate).to.be.undefined; 27 | }); 28 | 29 | it('should return the matched certificate', function () { 30 | var matchedCertificate = certificateList.resolveOne('https://www.google.com'); 31 | 32 | expect(matchedCertificate.id).to.eql(1); 33 | }); 34 | 35 | it('should return undefined when no certificate is matched', function () { 36 | var matchedCertificate = certificateList.resolveOne('https://www.twitter.com'); 37 | 38 | expect(matchedCertificate).to.be.undefined; 39 | }); 40 | 41 | it('should exit safely when called with url which is not a string or a Url', function () { 42 | var matchedCertificate = certificateList.resolveOne({}); 43 | 44 | expect(matchedCertificate).to.be.undefined; 45 | 46 | matchedCertificate = certificateList.resolveOne(['bla']); 47 | 48 | expect(matchedCertificate).to.be.undefined; 49 | }); 50 | }); 51 | 52 | describe('isCertificateList', function () { 53 | it('should return true for CertificateList instance', function () { 54 | var certificateList = new CertificateList({}, [{ matches: [] }]); 55 | 56 | expect(CertificateList.isCertificateList()).to.be.false; 57 | expect(CertificateList.isCertificateList(certificateList)).to.be.true; 58 | expect(CertificateList.isCertificateList({})).to.be.false; 59 | expect(CertificateList.isCertificateList({ _postman_propertyName: 'CertificateList' })).to.be.false; 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/unit/certificate.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | sdk = require('../../lib/index.js'), 3 | Certificate = sdk.Certificate; 4 | 5 | describe('Certificate', function () { 6 | describe('constructor', function () { 7 | it('should allow creating an empty Certificate', function () { 8 | var certificate = new Certificate(); 9 | 10 | expect(certificate).to.not.eql(undefined); 11 | expect(certificate._postman_propertyName).to.not.eql('Certificate'); 12 | }); 13 | 14 | it('should correctly identify Certificate instances', function () { 15 | var certificate = new Certificate(); 16 | 17 | expect(Certificate.isCertificate(certificate)).to.be.true; 18 | expect(Certificate.isCertificate({})).to.be.false; 19 | expect(Certificate.isCertificate({ _postman_propertyName: 'certificate' })).to.be.false; 20 | }); 21 | }); 22 | 23 | describe('canApplyTo', function () { 24 | it('should return false for empty target URLs', function () { 25 | var certificate = new Certificate({ matches: ['https://google.com/*'] }); 26 | 27 | expect(certificate.canApplyTo('')).to.be.false; 28 | expect(certificate.canApplyTo(new sdk.Url())).to.be.false; 29 | }); 30 | 31 | it('should return true only when tested with a match', function () { 32 | var certificate = new Certificate({ matches: ['https://google.com/*'] }); 33 | 34 | expect(certificate.canApplyTo('https://www.google.com')).to.be.false; 35 | expect(certificate.canApplyTo('https://example.com')).to.be.false; 36 | expect(certificate.canApplyTo('https://google.com')).to.be.true; 37 | }); 38 | 39 | it('should return true when tested with a match on any of the allowed matches', function () { 40 | var certificate = new Certificate({ matches: ['https://google.com/*', 'https://*.example.com/*'] }); 41 | 42 | expect(certificate.canApplyTo('https://twitter.com')).to.be.false; 43 | expect(certificate.canApplyTo('https://foo.example.com')).to.be.true; 44 | expect(certificate.canApplyTo('https://google.com')).to.be.true; 45 | }); 46 | 47 | it('should disallow test string with protocol not http or https', function () { 48 | var certificate = new Certificate({ matches: ['http://*'], server: 'https://proxy.com/' }); 49 | 50 | expect(certificate.canApplyTo('foo://www.google.com')).to.be.false; 51 | expect(certificate.canApplyTo('file://www.google.com')).to.be.false; 52 | }); 53 | 54 | it('should disallow any test string when match protocol is not http or https', function () { 55 | var certificate = new Certificate({ matches: ['ftp://*'], server: 'https://proxy.com/' }); 56 | 57 | expect(certificate.canApplyTo('foo://www.google.com')).to.be.false; 58 | expect(certificate.canApplyTo('file://www.google.com')).to.be.false; 59 | expect(certificate.canApplyTo('http://www.google.com')).to.be.false; 60 | expect(certificate.canApplyTo('https://www.google.com')).to.be.false; 61 | }); 62 | 63 | it('should match given port correctly', function () { 64 | var certificate = new Certificate({ matches: ['https://example.com:8080/*'] }); 65 | 66 | expect(certificate.canApplyTo('https://example.com:443')).to.be.false; 67 | expect(certificate.canApplyTo('https://example.com:8080')).to.be.true; 68 | }); 69 | 70 | it('should match all the ports correctly', function () { 71 | var certificate = new Certificate({ matches: ['https://example.com:*/*'] }); 72 | 73 | expect(certificate.canApplyTo('https://example.com')).to.be.true; 74 | expect(certificate.canApplyTo('https://example.com:443')).to.be.true; 75 | expect(certificate.canApplyTo('https://example.com:8080')).to.be.true; 76 | }); 77 | 78 | it('should implicitly match port 443 if given in Url', function () { 79 | var certificate = new Certificate({ matches: ['https://example.com/*'] }); 80 | 81 | expect(certificate.canApplyTo('https://example.com')).to.be.true; 82 | expect(certificate.canApplyTo('https://example.com:443')).to.be.true; 83 | expect(certificate.canApplyTo('https://example.com:8080')).to.be.false; 84 | }); 85 | 86 | it('should implicitly match port 443 if given in matches', function () { 87 | var certificate = new Certificate({ matches: ['https://example.com:443/*'] }); 88 | 89 | expect(certificate.canApplyTo('https://example.com')).to.be.true; 90 | expect(certificate.canApplyTo('https://example.com:443')).to.be.true; 91 | expect(certificate.canApplyTo('https://example.com:8080')).to.be.false; 92 | }); 93 | 94 | it('should match non 443 port correctly', function () { 95 | var certificate = new Certificate({ matches: ['https://example.com:8080/*'] }); 96 | 97 | expect(certificate.canApplyTo('https://example.com')).to.be.false; 98 | expect(certificate.canApplyTo('https://example.com:443')).to.be.false; 99 | expect(certificate.canApplyTo('https://example.com:8080')).to.be.true; 100 | }); 101 | }); 102 | 103 | describe('toJSON', function () { 104 | it('should contain host property', function () { 105 | var rawCert = { 106 | matches: ['https://postman-echo.com/*', 'https://bla.com/*'], 107 | key: { src: '/Users/here' }, 108 | cert: { src: '/Users/here' }, 109 | pfx: { src: '/Users/here' }, 110 | passphrase: 'iamhere' 111 | }, 112 | certificate = new Certificate(rawCert), 113 | serialisedCertificate = certificate.toJSON(); 114 | 115 | expect(serialisedCertificate).to.deep.include({ 116 | key: rawCert.key, 117 | cert: rawCert.cert, 118 | pfx: rawCert.pfx, 119 | passphrase: rawCert.passphrase 120 | }); 121 | }); 122 | 123 | it('should not contain key value', function () { 124 | var rawCert = { 125 | matches: ['https://postman-echo.com/*', 'https://bla.com/*'], 126 | key: { src: '/Users/here' }, 127 | cert: { src: '/Users/here' }, 128 | pfx: { src: '/Users/here' }, 129 | passphrase: 'iamhere' 130 | }, 131 | certificate = new Certificate(rawCert), 132 | serialisedCertificate; 133 | 134 | certificate.key.value = 'something'; 135 | certificate.cert.value = 'something-else'; 136 | certificate.pfx.value = 'something-else-as-well'; 137 | serialisedCertificate = certificate.toJSON(); 138 | 139 | expect(serialisedCertificate.key).to.not.have.property('value'); 140 | expect(serialisedCertificate.cert).to.not.have.property('value'); 141 | expect(serialisedCertificate.pfx).to.not.have.property('value'); 142 | }); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /test/unit/cookie-list.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | CookieList = require('../../lib/index.js').CookieList; 3 | 4 | describe('CookieList', function () { 5 | describe('.isCookieList', function () { 6 | it('should correctly identify a CookieList instance', function () { 7 | var cookieList = new CookieList({}, [{ 8 | domain: '.httpbin.org', 9 | expires: 1502442248, 10 | hostOnly: false, 11 | httpOnly: false, 12 | key: '_ga', 13 | path: '/', 14 | secure: false, 15 | session: false, 16 | _postman_storeId: '0', 17 | value: 'GA1.2.113558537.1435817423' 18 | }]); 19 | 20 | expect(CookieList.isCookieList()).to.be.false; 21 | expect(CookieList.isCookieList(cookieList)).to.be.true; 22 | expect(CookieList.isCookieList({})).to.be.false; 23 | expect(CookieList.isCookieList({ _postman_propertyName: 'CookieList' })).to.be.false; 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/unit/description.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | fixtures = require('../fixtures'), 3 | Description = require('../../lib/index.js').Description; 4 | 5 | describe('Description', function () { 6 | var rawDescription = fixtures.collectionV2.item[0].description, 7 | description = new Description(rawDescription); 8 | 9 | describe('sanity', function () { 10 | it('initializes successfully', function () { 11 | expect(description).to.be.ok; 12 | }); 13 | 14 | describe('has property', function () { 15 | it('content', function () { 16 | expect(description).to.have.property('content', rawDescription.content); 17 | }); 18 | 19 | it('type', function () { 20 | expect(description).to.have.property('type', 'text/plain'); 21 | }); 22 | }); 23 | 24 | describe('has method', function () { 25 | it('Stringificaton (toString)', function () { 26 | expect(description).to.have.property('toString').that.is.a('function'); 27 | }); 28 | }); 29 | }); 30 | 31 | describe('json representation', function () { 32 | it('must match what the description was initialized with', function () { 33 | var jsonified = description.toJSON(); 34 | 35 | expect(jsonified).to.deep.include({ 36 | content: rawDescription.content, 37 | type: rawDescription.type || 'text/plain' 38 | }); 39 | }); 40 | }); 41 | 42 | describe('isDescription', function () { 43 | var rawDescription = { 44 | content: '

This is H1

italic ', 45 | version: '2.0.1-abc+efg' 46 | }; 47 | 48 | it('should return true for a description instance', function () { 49 | expect(Description.isDescription(new Description(rawDescription))).to.be.true; 50 | }); 51 | 52 | it('should return false for a raw description object', function () { 53 | expect(Description.isDescription(rawDescription)).to.be.false; 54 | }); 55 | 56 | it('should return false when called without arguments', function () { 57 | expect(Description.isDescription()).to.be.false; 58 | }); 59 | }); 60 | 61 | describe('.toString', function () { 62 | it('should return stringified description content', function () { 63 | var description = new Description({ 64 | content: '<%= Description %>', 65 | type: 'text/plain' 66 | }); 67 | 68 | expect(description.toString()).to.equal('<%= Description %>'); 69 | }); 70 | 71 | it('should return an empty string for falsy input', function () { 72 | expect(new Description().toString()).to.equal(''); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/unit/dynamic-variables.test.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | dynamicVariables = require('../../lib/superstring/dynamic-variables'), 3 | expect = require('chai').expect; 4 | 5 | describe('Dynamic variable', function () { 6 | it('should have all attributes', function () { 7 | expect(dynamicVariables).to.be.an('object'); 8 | _.forOwn(dynamicVariables, function (dynamicVariable) { 9 | expect(dynamicVariable).to.be.an('object'); 10 | expect(dynamicVariable.description).to.be.a('string'); 11 | expect(dynamicVariable.generator).to.be.a('function'); 12 | }); 13 | }); 14 | 15 | describe('generator', function () { 16 | it('should return random data', function () { 17 | _.forOwn(dynamicVariables, function (variable) { 18 | expect(variable.generator()).to.not.be.undefined; 19 | }); 20 | }); 21 | 22 | it('should start with $', function () { 23 | _.forOwn(dynamicVariables, function (variable, name) { 24 | expect(name[0]).to.equal('$'); 25 | }); 26 | }); 27 | 28 | it('$randomInt should return a random number', function () { 29 | expect(dynamicVariables.$randomInt.generator()).to.be.within(0, 1000); 30 | }); 31 | 32 | it('$timeStamp should return a valid timestamp', function () { 33 | expect(dynamicVariables.$timestamp.generator()).to.be.a('number'); 34 | }); 35 | 36 | it('$isoTimestamp returns a timestamp in ISO format', function () { 37 | var isoTimestamp = dynamicVariables.$isoTimestamp.generator(), 38 | // eslint-disable-next-line security/detect-non-literal-regexp 39 | regex = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})' + // match year 40 | '-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])' + // match month and day 41 | 'T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z)?$'); // match time 42 | 43 | expect(isoTimestamp).to.be.a('string'); 44 | expect(isoTimestamp).to.match(regex); 45 | }); 46 | 47 | it('$guid should return a valid uuid', function () { 48 | expect(dynamicVariables.$guid.generator()) 49 | .to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i); 50 | }); 51 | 52 | it('$randomPhoneNumber returns a random phone number without extension', function () { 53 | var phone1 = dynamicVariables.$randomPhoneNumber.generator(), 54 | phone2 = dynamicVariables.$randomPhoneNumber.generator(); 55 | 56 | expect(phone1.length).to.equal(12); 57 | expect(phone2.length).to.equal(12); 58 | expect(phone1).to.not.equal(phone2); 59 | }); 60 | 61 | it('$randomLocale returns a random locale', function () { 62 | var locale1 = dynamicVariables.$randomLocale.generator(), 63 | locale2 = dynamicVariables.$randomLocale.generator(); 64 | 65 | expect(locale1.length).to.be.at.least(2).and.at.most(3); 66 | expect(locale2.length).to.be.at.least(2).and.at.most(3); 67 | expect(locale1).to.not.equal(locale2); 68 | }); 69 | 70 | it('$randomPhoneNumberExt returns a random phone number with extension', function () { 71 | var phone1 = dynamicVariables.$randomPhoneNumberExt.generator(), 72 | phone2 = dynamicVariables.$randomPhoneNumberExt.generator(); 73 | 74 | expect(phone1.length).to.be.at.least(14); 75 | expect(phone2.length).to.be.at.least(14); 76 | expect(phone1).to.not.equal(phone2); 77 | }); 78 | 79 | it('$randomWords returns some random numbers', function () { 80 | var words = dynamicVariables.$randomWords.generator(), 81 | wordsArray = words.split(' '); 82 | 83 | expect(words).to.not.be.null; 84 | expect(wordsArray.length).to.be.at.least(2); 85 | }); 86 | 87 | it('$randomFilePath returns a file path', function () { 88 | var filePath = dynamicVariables.$randomFilePath.generator(); 89 | 90 | expect(filePath).to.not.be.undefined; 91 | expect(filePath).to.not.be.null; 92 | }); 93 | 94 | it('$randomDirectoryPath returns a directory path', function () { 95 | var directoryPath = dynamicVariables.$randomDirectoryPath.generator(); 96 | 97 | expect(directoryPath).to.not.be.undefined; 98 | expect(directoryPath).to.not.be.null; 99 | }); 100 | 101 | it('$randomAvatarImage returns a random avatar image', function () { 102 | var avatarImage = dynamicVariables.$randomAvatarImage.generator(); 103 | 104 | expect(avatarImage).to.be.a('string') 105 | .and.match(/^https:\/\/(avatars\.githubusercontent\.com|cloudflare-ipfs\.com)\/.+/); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /test/unit/event.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | fixtures = require('../fixtures'), 3 | sdk = require('../../lib/index.js'), 4 | 5 | Event = sdk.Event, 6 | Script = sdk.Script; 7 | 8 | describe('Event', function () { 9 | var rawEvent = { 10 | listen: 'test', 11 | id: 'my-global-script-1', 12 | script: { 13 | type: 'text/javascript', 14 | exec: 'console.log("hello");', 15 | packages: {} 16 | } 17 | }; 18 | 19 | describe('sanity', function () { 20 | describe('global event', function () { 21 | var rawEvent = fixtures.collectionV2.event[0], 22 | postmanEvent = new Event(rawEvent); 23 | 24 | it('initializes successfully', function () { 25 | expect(postmanEvent).to.be.ok; 26 | }); 27 | 28 | describe('has property', function () { 29 | it('listen', function () { 30 | expect(postmanEvent).to.have.property('listen', 'test'); 31 | }); 32 | 33 | it('script', function () { 34 | expect(postmanEvent).to.have.property('script'); 35 | }); 36 | }); 37 | }); 38 | 39 | describe('inline event', function () { 40 | var rawEvent = fixtures.collectionV2.event[1], 41 | postmanEvent = new Event(rawEvent); 42 | 43 | it('initializes successfully', function () { 44 | expect(postmanEvent).to.be.ok; 45 | }); 46 | 47 | describe('has property', function () { 48 | it('listen', function () { 49 | expect(postmanEvent).to.have.property('listen', 'prerequest'); 50 | }); 51 | 52 | it('script', function () { 53 | expect(postmanEvent).to.have.property('script').that.is.an('object'); 54 | expect(postmanEvent.script).to.have.property('type', 'text/javascript'); 55 | expect(postmanEvent.script).to.have.property('exec').that.is.an('array'); 56 | expect(postmanEvent.script).to.have.property('packages').that.is.an('object'); 57 | }); 58 | }); 59 | 60 | it('should respect case for listen property', function () { 61 | var postmanEvent = new Event({ id: 'dummy-id', listen: 'preRequest' }); 62 | 63 | expect(postmanEvent).to.have.property('listen', 'preRequest'); 64 | }); 65 | }); 66 | }); 67 | 68 | describe('json representation', function () { 69 | it('must match what the event was initialized with', function () { 70 | var event = new Event(rawEvent), 71 | jsonified = event.toJSON(); 72 | 73 | expect(jsonified.listen).to.eql(event.listen); 74 | // Script property checking is done independently 75 | expect(jsonified).to.have.property('script'); 76 | }); 77 | }); 78 | 79 | describe('update', function () { 80 | var script = 'console.log("This is a test log");'; 81 | 82 | it('must work with script instance definitions', function () { 83 | var event = new Event(rawEvent); 84 | 85 | event.update({ script: new Script({ exec: script }) }); 86 | expect(event.toJSON().script.exec).to.eql([script]); 87 | }); 88 | 89 | it('must work with script strings', function () { 90 | var event = new Event(rawEvent); 91 | 92 | event.update({ script }); 93 | expect(event.toJSON().script.exec).to.eql([script]); 94 | }); 95 | 96 | it('must work with script arrays', function () { 97 | var event = new Event(rawEvent); 98 | 99 | event.update({ script: [script] }); 100 | expect(event.toJSON().script.exec).to.eql([script]); 101 | }); 102 | 103 | it('must correctly handle invalid/undefined script input', function () { 104 | var event = new Event(rawEvent); 105 | 106 | event.update(); 107 | expect(event.toJSON().script.exec).to.eql([rawEvent.script.exec]); 108 | }); 109 | 110 | it('must correctly handle non-compliant script input', function () { 111 | var event = new Event(rawEvent), 112 | eventJSON; 113 | 114 | event.update({}); 115 | 116 | eventJSON = event.toJSON(); 117 | 118 | expect(eventJSON).to.deep.include({ 119 | id: 'my-global-script-1', 120 | listen: 'test' 121 | }); 122 | expect(eventJSON).to.have.property('script').that.has.property('id'); 123 | expect(eventJSON.script).to.deep.include({ 124 | type: 'text/javascript', 125 | exec: ['console.log("hello");'], 126 | packages: {} 127 | }); 128 | }); 129 | 130 | it('should update script id', function () { 131 | var event = new Event(rawEvent), 132 | beforeJSON = event.toJSON(), 133 | afterJSON; 134 | 135 | expect(beforeJSON).to.have.property('script').that.has.property('id'); 136 | expect(beforeJSON.script).to.deep.include({ 137 | type: 'text/javascript', 138 | exec: ['console.log("hello");'], 139 | packages: {} 140 | }); 141 | 142 | event.update({ script: { id: 'my-new-script' } }); 143 | afterJSON = event.toJSON(); 144 | 145 | expect(beforeJSON.script.id).to.not.equal(afterJSON.script.id); 146 | expect(afterJSON.script).to.have.property('id', 'my-new-script'); 147 | expect(afterJSON.script.exec).to.be.undefined; 148 | }); 149 | 150 | it('should not add packages key if not present', function () { 151 | var rawEvent = { 152 | listen: 'test', 153 | id: 'my-global-script-1', 154 | script: { 155 | type: 'text/javascript', 156 | exec: 'console.log("hello");' 157 | } 158 | }, 159 | event = new Event(rawEvent), 160 | beforeJSON = event.toJSON(), 161 | afterJSON; 162 | 163 | expect(beforeJSON.script).to.not.have.property('packages'); 164 | 165 | event.update({ script: { exec: 'console.log("updated");' } }); 166 | afterJSON = event.toJSON(); 167 | 168 | expect(afterJSON.script).to.not.have.property('packages'); 169 | }); 170 | }); 171 | 172 | describe('isEvent', function () { 173 | var rawEvent = { 174 | listen: 'test', 175 | id: 'my-global-script-1', 176 | script: { 177 | type: 'text/javascript', 178 | exec: 'console.log("hello");' 179 | } 180 | }; 181 | 182 | it('should return true for an Event instance', function () { 183 | expect(Event.isEvent(new Event(rawEvent))).to.be.true; 184 | }); 185 | 186 | it('should return false for a raw Event object', function () { 187 | expect(Event.isEvent(rawEvent)).to.be.false; 188 | }); 189 | 190 | it('should return false when called without arguments', function () { 191 | expect(Event.isEvent()).to.be.false; 192 | }); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /test/unit/form-param.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | FormParam = require('../../lib/index.js').FormParam; 3 | 4 | describe('FormParam', function () { 5 | describe('constructor', function () { 6 | it('should correctly construct a FormParam instance', function () { 7 | var raw = { key: 'foo', value: 'bar' }, 8 | fp = new FormParam(raw); 9 | 10 | expect(fp.toJSON()).to.eql(raw); 11 | }); 12 | 13 | it('should default to empty strings for keys and values if none are provided', function () { 14 | var fp = new FormParam(); 15 | 16 | expect(fp.toJSON()).to.eql({ key: '', value: '' }); 17 | }); 18 | }); 19 | 20 | describe('.toString', function () { 21 | it('should unparse the FormParam instance correctly', function () { 22 | var fp = new FormParam({ key: 'foo', value: 'bar' }); 23 | 24 | expect(fp.toString()).to.equal('foo=bar'); 25 | }); 26 | }); 27 | 28 | describe('contentType', function () { 29 | it('should accept contentType for type `file`', function () { 30 | var fp = new FormParam({ 31 | key: 'fileName', 32 | src: 'fileSrc', 33 | contentType: 'text/csv', 34 | type: 'file' 35 | }); 36 | 37 | expect(fp.toJSON()).to.have.property('contentType', 'text/csv'); 38 | }); 39 | 40 | it('should accept contentType for type `text`', function () { 41 | var fp = new FormParam({ 42 | key: 'data', 43 | value: '{"foo": "bar"}', 44 | contentType: 'application/json', 45 | type: 'text' 46 | }); 47 | 48 | expect(fp.toJSON()).to.have.property('contentType', 'application/json'); 49 | }); 50 | 51 | it('should default to undefined if contentType is not provided', function () { 52 | var fp = new FormParam({ key: 'foo', value: 'bar' }); 53 | 54 | expect(fp).to.have.property('contentType', undefined); 55 | }); 56 | }); 57 | 58 | describe('.toJSON', function () { 59 | it('should correctly handle param with type `text`', function () { 60 | var definition = { 61 | key: 'foo', 62 | value: 'bar', 63 | type: 'text', 64 | contentType: 'application/json' 65 | }, 66 | fp = new FormParam(definition); 67 | 68 | expect(fp.toJSON()).to.eql(definition); 69 | }); 70 | 71 | it('should correctly handle param with type `file`', function () { 72 | var definition = { 73 | key: 'foo', 74 | src: 'fileSrc', 75 | type: 'file', 76 | fileName: 'file.txt', 77 | contentType: 'application/json' 78 | }, 79 | fp = new FormParam(definition); 80 | 81 | expect(fp.toJSON()).to.eql(definition); 82 | }); 83 | 84 | it('should not have non-string value for param with type `file`', function () { 85 | var fp = new FormParam({ 86 | key: 'foo', 87 | value: 'bar', 88 | type: 'file', 89 | contentType: 'application/json', 90 | src: 'fileSrc' 91 | }); 92 | 93 | expect(fp.toJSON()).to.have.property('value', 'bar'); 94 | 95 | fp = new FormParam({ 96 | key: 'foo', 97 | value: Buffer.from('bar'), 98 | type: 'file', 99 | contentType: 'application/json', 100 | src: 'fileSrc' 101 | }); 102 | 103 | expect(fp.toJSON()).to.not.have.property('value'); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /test/unit/header-list.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | sdk = require('../../lib/index.js'), 3 | 4 | HeaderList = sdk.HeaderList; 5 | 6 | describe('HeaderList', function () { 7 | it('should be a working constructor', function () { 8 | expect(new HeaderList()).to.be.an.instanceOf(HeaderList); 9 | }); 10 | 11 | it('should be able to initialise with header as string', function () { 12 | var hl = new HeaderList(null, 'Accept: *\nContent-Type: text/html'); 13 | 14 | expect(hl.count()).to.equal(2); 15 | }); 16 | 17 | it('should be able to export headers to string', function () { 18 | var hl = new HeaderList(null, 'Accept: *\nContent-Type: text/html'); 19 | 20 | expect(hl.toString()).to.equal('Accept: *\r\nContent-Type: text/html\r\n'); 21 | }); 22 | 23 | describe('.contentSize', function () { 24 | it('should be able to return header size', function () { 25 | var hl = new HeaderList(null, 'Accept: *\nContent-Type: text/html'); 26 | 27 | expect(hl.contentSize(200, 'OK')).to.equal(36); 28 | }); 29 | 30 | it('should return 0 for an empty header set', function () { 31 | var hl = new HeaderList(); 32 | 33 | expect(hl.contentSize()).to.equal(0); 34 | }); 35 | 36 | it('should should handle disabled and falsy headers correctly', function () { 37 | var hl = new HeaderList(null, [{ 38 | key: undefined, 39 | value: 'bar' 40 | }, { 41 | key: 'foo', 42 | value: 'bar', 43 | disabled: true 44 | }]); 45 | 46 | expect(hl.contentSize()).to.equal(7); // ": bar + CRLF" 47 | }); 48 | }); 49 | 50 | describe('.isHeaderList', function () { 51 | it('should correctly identify HeaderList instances', function () { 52 | var headerList = new HeaderList(null, 'Accept: *\nContent-Type: text/html'); 53 | 54 | expect(HeaderList.isHeaderList()).to.be.false; 55 | expect(HeaderList.isHeaderList(headerList)).to.be.true; 56 | expect(HeaderList.isHeaderList({})).to.be.false; 57 | expect(HeaderList.isHeaderList({ _postman_propertyName: 'headerList' })).to.be.false; 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/unit/response-mime.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | Response = require('../../lib/index.js').Response; 3 | 4 | describe('response mime', function () { 5 | it('must treat lack of information as plain text', function () { 6 | var response = new Response(); 7 | 8 | expect(response.contentInfo()).to.include({ 9 | mimeType: 'text', 10 | mimeFormat: 'plain', 11 | charset: 'utf8', 12 | fileExtension: 'txt', 13 | fileName: 'response.txt' 14 | }); 15 | }); 16 | 17 | it('must translate ambiguous content type', function () { 18 | var response = new Response({ 19 | header: [{ 20 | key: 'content-type', 21 | value: 'application/json' 22 | }] 23 | }); 24 | 25 | expect(response.contentInfo()).to.include({ 26 | mimeType: 'text', 27 | mimeFormat: 'json', 28 | charset: 'utf8', 29 | fileExtension: 'json', 30 | fileName: 'response.json' 31 | }); 32 | }); 33 | 34 | it('must translate malformed content type', function () { 35 | var response = new Response({ 36 | header: [{ 37 | key: 'content-type', 38 | value: ' application / hal+ json ' 39 | }] 40 | }); 41 | 42 | expect(response.contentInfo()).to.include({ 43 | mimeType: 'text', 44 | mimeFormat: 'json', 45 | charset: 'utf8', 46 | fileName: 'response' 47 | }); 48 | }); 49 | 50 | it('must translate content type with charset', function () { 51 | var response = new Response({ 52 | header: [{ 53 | key: 'content-type', 54 | value: 'application/ogg; charset=utf8' 55 | }] 56 | }); 57 | 58 | expect(response.contentInfo()).to.include({ 59 | mimeType: 'audio', 60 | mimeFormat: 'ogg', 61 | charset: 'utf8', 62 | fileExtension: 'ogx', 63 | fileName: 'response.ogx' 64 | }); 65 | }); 66 | 67 | it('must be greedy in assuming script types', function () { 68 | var response = new Response({ 69 | header: [{ 70 | key: 'content-type', 71 | value: 'application/x-ecmascript' 72 | }] 73 | }); 74 | 75 | expect(response.contentInfo()).to.include({ 76 | mimeType: 'text', 77 | mimeFormat: 'script', 78 | charset: 'utf8', 79 | fileName: 'response' 80 | }); 81 | }); 82 | 83 | it('must be greedy in assuming xml types', function () { 84 | var response = new Response({ 85 | header: [{ 86 | key: 'content-type', 87 | // customer reported 88 | value: 'application/vnd.yamaha.openscoreformat.osfpvg+xml' 89 | }] 90 | }); 91 | 92 | expect(response.contentInfo()).to.include({ 93 | mimeType: 'text', 94 | mimeFormat: 'xml', 95 | charset: 'utf8', 96 | fileExtension: 'osfpvg', 97 | fileName: 'response.osfpvg' 98 | }); 99 | 100 | // customer reported 101 | // reusing the same response object 102 | response.headers.one('content-type').value = 'application/vnd.route66.link66+xml'; 103 | 104 | expect(response.contentInfo()).to.include({ 105 | mimeType: 'text', 106 | mimeFormat: 'xml', 107 | charset: 'utf8', 108 | fileExtension: 'link66', 109 | fileName: 'response.link66' 110 | }); 111 | }); 112 | 113 | it('must gracefully handle extremely deviant content type', function () { 114 | var response = new Response({ 115 | header: [{ 116 | key: 'content-type', 117 | value: 'machine/samaritan' 118 | }] 119 | }); 120 | 121 | expect(response.contentInfo()).to.include({ 122 | mimeType: 'unknown', 123 | mimeFormat: 'raw', 124 | charset: 'utf8', 125 | fileName: 'response' 126 | }); 127 | }); 128 | 129 | it('must detect mime from body if content-type is missing', function () { 130 | var sampleArray = [0xFF, 0xD8, 0xFF, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72], 131 | response = new Response({ 132 | // todo load real file content here (maybe 1x1 px bmp) 133 | stream: Buffer.from(new Uint32Array(sampleArray)) 134 | }); 135 | 136 | expect(response.contentInfo()).to.include({ 137 | mimeType: 'image', 138 | mimeFormat: 'image', 139 | charset: 'utf8', 140 | fileExtension: 'jpg', 141 | fileName: 'response.jpg' 142 | }); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /test/unit/superstring.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | SuperString = require('../../lib/superstring').SuperString, 3 | Substitutor = require('../../lib/superstring').Substitutor; 4 | 5 | describe('String utilities', function () { 6 | describe('SuperString', function () { 7 | describe('sanity', function () { 8 | it('should correctly default to the .toString value for non string types', function () { 9 | var supStr = new SuperString([1, 2, 3]); 10 | 11 | expect(supStr.valueOf()).to.equal('1,2,3'); 12 | }); 13 | 14 | it('should correctly default to an empty string as a fallback', function () { 15 | var supStr = new SuperString(Object.create(null)); // A null prototyped object does not have .toString 16 | 17 | expect(supStr.valueOf()).to.equal(''); 18 | }); 19 | }); 20 | }); 21 | 22 | describe('Substitutor', function () { 23 | describe('default vars', function () { 24 | it('should not be empty', function () { 25 | expect(Substitutor.DEFAULT_VARS).to.not.be.empty; 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/unit/url-match-pattern-list.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | UrlMatchPatternList = require('../../lib/url-pattern/url-match-pattern-list').UrlMatchPatternList, 3 | 4 | // return a target to run specs on 5 | getTargetForSpec = function (matchPatternString) { 6 | return new UrlMatchPatternList({}, [matchPatternString]); 7 | }, 8 | 9 | // We run every spec for test method on UrlMatchPattern and UrlMatchPatternList 10 | // because the UrlMatchPatternList.prototype.test does not internally use 11 | // UrlMatchPattern.prototype.test. 12 | // So we have to run the spec on both pattern.test and patternList.test 13 | // to ensure they behave the same. 14 | specForTestMethodOnTarget = require('./url-match-pattern.test.js').specForTestMethodOnTarget; 15 | 16 | describe('UrlMatchPatternList', function () { 17 | describe('test', function () { 18 | specForTestMethodOnTarget(getTargetForSpec); 19 | 20 | it('should match for any', function () { 21 | var matchPatternList = new UrlMatchPatternList({}, ['https://example.com/*']); 22 | 23 | expect(matchPatternList.test('https://example.com')).to.be.true; 24 | expect(matchPatternList.test('https://example.com/')).to.be.true; 25 | expect(matchPatternList.test('https://example.com:443')).to.be.true; 26 | expect(matchPatternList.test('https://www.example.com/')).to.be.false; 27 | expect(matchPatternList.test('https://example.com/hello')).to.be.true; 28 | expect(matchPatternList.test('https://example.com/foo/bar')).to.be.true; 29 | expect(matchPatternList.test('https://foo.example.com')).to.be.false; 30 | expect(matchPatternList.test('https://foo.com')).to.be.false; 31 | }); 32 | it('should match any url for ', function () { 33 | var matchPatternList = new UrlMatchPatternList({}, ['']); 34 | 35 | expect(matchPatternList.test('https://google.com')).to.be.true; 36 | expect(matchPatternList.test('https://www.google.com')).to.be.true; 37 | expect(matchPatternList.test('https://example.com')).to.be.true; 38 | expect(matchPatternList.test('https://foo.com')).to.be.true; 39 | }); 40 | it('should match url is pattern list', function () { 41 | var matchPatternList = new UrlMatchPatternList({}, ['https://google.com/*', 'https://example.com/*']); 42 | 43 | expect(matchPatternList.test('https://google.com')).to.be.true; 44 | expect(matchPatternList.test('https://example.com')).to.be.true; 45 | expect(matchPatternList.test('https://foo.com')).to.be.false; 46 | }); 47 | }); 48 | 49 | describe('toJSON', function () { 50 | it('should return [] when called on an empty UrlMatchPatternList', function () { 51 | var patternList = new UrlMatchPatternList(); 52 | 53 | expect(patternList.toJSON()).to.eql([]); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/unit/util.test.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | path = require('path'), 3 | btoa = require('btoa'), 4 | expect = require('chai').expect, 5 | util = require('../../lib/util.js'), 6 | 7 | nodeIt = typeof window === 'undefined' ? it : it.skip; 8 | 9 | describe('SDK Utils', function () { 10 | describe('lodash mixin', function () { 11 | describe('.inherit', function () { 12 | it('should correctly handle non function bases', function () { 13 | var result = util.lodash.inherit({}, null); 14 | 15 | expect(result.super_).to.equal(_.noop); 16 | }); 17 | }); 18 | 19 | describe('.args', function () { 20 | it('should correctly convert an arguments instance to an array equivalent', function () { 21 | var result = util.lodash.args(arguments, null); 22 | 23 | expect(result).to.eql([]); 24 | }); 25 | }); 26 | 27 | describe('.assignLocked', function () { 28 | it('should correctly create locked assignments when called', function () { 29 | var result = util.lodash.assignLocked({}, 'foo', 'bar'), 30 | descriptor = Object.getOwnPropertyDescriptor(result, 'foo'); 31 | 32 | expect(descriptor).to.eql({ 33 | value: 'bar', 34 | configurable: false, 35 | enumerable: false, 36 | writable: false 37 | }); 38 | }); 39 | }); 40 | 41 | describe('.randomString', function () { 42 | it('should use a length of 6 by default', function () { 43 | expect(util.lodash.randomString()).to.match(/^[A-z0-9]{6}$/); 44 | }); 45 | }); 46 | 47 | describe('.ensureEncoded', function () { 48 | it('should encode correctly', function () { 49 | expect(util.lodash.ensureEncoded('hello world')).to.equal('hello%20world'); 50 | }); 51 | 52 | it('should not double encode', function () { 53 | expect(util.lodash.ensureEncoded('hello%20world')).to.equal('hello%20world'); 54 | }); 55 | 56 | it('should handle malformed URI sequence on decode', function () { 57 | expect(util.lodash.ensureEncoded('%E0%A4%A')).to.equal('%25E0%25A4%25A'); 58 | }); 59 | 60 | it('should handle malformed URI sequence on encode', function () { 61 | expect(util.lodash.ensureEncoded('\uD800')).to.equal('\uD800'); 62 | }); 63 | }); 64 | }); 65 | 66 | describe('.btoa', function () { 67 | it('should work correctly under regular conditions', function () { 68 | expect(util.btoa('randomString')).to.equal('cmFuZG9tU3RyaW5n'); 69 | }); 70 | 71 | (typeof window === 'undefined' ? describe : describe.skip)('special cases', function () { 72 | var util, 73 | cleanup = false; 74 | 75 | before(function () { 76 | if (!global.btoa) { 77 | global.btoa = btoa; 78 | cleanup = true; 79 | } 80 | delete require.cache[path.resolve('lib/util.js')]; 81 | util = require('../../lib/util'); 82 | }); 83 | after(function () { cleanup && delete global.btoa; }); 84 | 85 | it('should use the provided btoa implementation when applicable', function () { 86 | expect(util.btoa('randomString')).to.equal('cmFuZG9tU3RyaW5n'); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('.arrayBufferToString', function () { 92 | it('should correctly convert array buffers to strings', function () { 93 | expect(util.arrayBufferToString(Buffer.from('random'))).to.equal('random'); 94 | }); 95 | }); 96 | 97 | describe('.bufferOrArrayBufferToString', function () { 98 | it('should bail out for non-buffer/string arguments', function () { 99 | expect(util.bufferOrArrayBufferToString('random')).to.equal('random'); 100 | }); 101 | 102 | it('should return an empty string for falsy arguments', function () { 103 | expect(util.bufferOrArrayBufferToString()).to.equal(''); 104 | }); 105 | 106 | it('should correctly convert array buffers to strings', function () { 107 | expect(util.bufferOrArrayBufferToString(Buffer.from('random'))).to.equal('random'); 108 | }); 109 | 110 | it('should handle default charsets correctly', function () { 111 | expect(util.bufferOrArrayBufferToString(Buffer.from('random'))).to.equal('random'); 112 | }); 113 | 114 | it('should handle charset overrides correctly', function () { 115 | expect(util.bufferOrArrayBufferToString(Buffer.from('random'), 'base64')).to.equal('cmFuZG9t'); 116 | }); 117 | 118 | it('should handle non buffer arguments correctly', function () { 119 | expect(util.bufferOrArrayBufferToString([])).to.equal(''); 120 | }); 121 | }); 122 | 123 | describe('.bufferOrArrayBufferToBase64', function () { 124 | it('should return an empty string for falsy arguments', function () { 125 | expect(util.bufferOrArrayBufferToBase64()).to.equal(''); 126 | }); 127 | 128 | nodeIt('should handle strings correctly', function () { 129 | expect(util.bufferOrArrayBufferToBase64('random')).to.equal('cmFuZG9t'); 130 | }); 131 | 132 | it('should handle buffers correctly', function () { 133 | expect(util.bufferOrArrayBufferToBase64(Buffer.from('random'))).to.equal('cmFuZG9t'); 134 | }); 135 | 136 | nodeIt('should handle ArrayBuffers correctly', function () { 137 | expect(util.bufferOrArrayBufferToBase64(new ArrayBuffer())).to.equal(''); 138 | }); 139 | 140 | it('should handle non buffer arguments correctly', function () { 141 | expect(util.bufferOrArrayBufferToBase64([])).to.equal(''); 142 | }); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /test/unit/version.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect, 2 | Version = require('../../lib/index.js').Version; 3 | 4 | describe('Version', function () { 5 | var rawVersion = '3.1.4-pi+rounded', 6 | version = new Version(rawVersion); 7 | 8 | it('parsed successfully', function () { 9 | expect(version).to.be.ok; 10 | expect(version).to.be.an('object'); 11 | }); 12 | 13 | describe('has properties', function () { 14 | it('build', function () { 15 | expect(version).to.have.property('build', 'rounded'); 16 | }); 17 | 18 | it('major', function () { 19 | expect(version).to.have.property('major', 3); 20 | }); 21 | 22 | it('minor', function () { 23 | expect(version).to.have.property('minor', 1); 24 | }); 25 | 26 | it('patch', function () { 27 | expect(version).to.have.property('patch', 4); 28 | }); 29 | 30 | it('prerelease', function () { 31 | expect(version).to.have.property('prerelease', 'pi'); 32 | }); 33 | 34 | it('raw', function () { 35 | expect(version).to.have.property('raw', rawVersion); 36 | }); 37 | 38 | it('string', function () { 39 | expect(version).to.have.property('string', '3.1.4-pi'); 40 | }); 41 | 42 | it('set', function () { 43 | expect(version.set).to.be.ok; 44 | expect(version.set).to.be.a('function'); 45 | }); 46 | }); 47 | 48 | describe('edge cases', function () { 49 | it('should not set if the provided options are invalid', function () { 50 | var version = new Version(); 51 | 52 | expect(version.toJSON()).to.be.empty; 53 | }); 54 | 55 | it('should default to a blank object if no version details are provided', function () { 56 | var version = new Version('0.0.0'); 57 | 58 | expect(version.toJSON()).to.eql({ 59 | raw: '0.0.0', 60 | major: 0, 61 | minor: 0, 62 | patch: 0, 63 | prerelease: [], 64 | build: [], 65 | string: '0.0.0' 66 | }); 67 | 68 | version.set(); 69 | expect(version.toJSON()).to.be.empty; 70 | }); 71 | 72 | it('should stringify the version instance correctly', function () { 73 | var v1 = new Version('0.0.0'), 74 | v2 = new Version(''); 75 | 76 | expect(v1.toString()).to.equal('0.0.0'); 77 | expect(v2.toString()).to.be.undefined; // not '', so that empty versions can be selectively pruned 78 | }); 79 | }); 80 | }); 81 | --------------------------------------------------------------------------------