Giter Club home page Giter Club logo

pactum's People

Contributors

ankurj00 avatar asaianudeep avatar aukevanleeuwen avatar baabouj avatar ca057 avatar depapp avatar dependabot[bot] avatar doriandevtech avatar jmcdo29 avatar leelaprasadv avatar lieutdan13 avatar mshangle1 avatar msharma-tw avatar rajaneeshkumar avatar rupert-mckay avatar samuelsantia avatar sudhakotni-git avatar tejeswarpatro18 avatar wine-fall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pactum's Issues

Mock listen on all interface 0.0.0.0

mock server is listen on local host by default 127.0.0.1.
Maybe I have not seen in the doc, how is it possible to listen on all interface 0.0.0.0 ?

Best Regards
Matthias

Support Custom Loggers

Background

PactumJS comes with a default custom built lightweight logger. This logger will serve the basic needs while running the pactum tests in local or a CI tool like Jenkins.

Description

For supporting advanced usage where tests might be running inside docker containers or server-less environments, we need a logger that fits well over there. This library tends to be lightweight and doesn't want to include multiple loggers. Instead it should provide a way for users to override the default logger. Use pactum.settings to override.

const { settings } = require('pactum');

settings.setLogger(myCustomLogger);

AC

  1. Custom Loggers are supported.
  2. Custom Loggers should support all the log methods.
  3. Custom Loggers should be able to respect log level.
  4. Add tests

expectJsonSnapshot() array objects validation

ExpectJsonSnapshot() doesn’t seem to validate objects in array.
I have an expected JSON like this:

{
  "components": [
    {
       "product": {
        "dairyProduct": {
          "id": "4f6e821a-ad7e-467d-9e92-3d01544215ff",
          "createdAt": "2022-01-19T23:04:27.009Z",
        },
      }
    }
  ]
}

I want to write a snapshot matcher like this:

.expectJsonSnapshot({
  "components": [
    {
      "product":
          "dairyProduct": {
            "id": uuid(),
            "createdAt": regex(1, DATA.REGEX.ISO_DATE),
          },
    }
  ],
}).name()

And it seems to always pass even if the products are different. E.g., it passes if I receive $.components[0].product.coffeeProduct instead of the expected $.components[0].product.dairyProduct

Can you please suggest if I need to use a specific matcher for the array objects?

OS: macOS Monterey 12.0.1
NPM: 8.1.0
Node: v17.0.1
Pactum: 3.0.21

Need an in-built logger to log response [to be displayed in console]

        const response = await pactum.spec()
            .post(endpoints.baseUrl + endpoints.requestShift)
            .withBody(body)
            .withHeaders('Accept', 'application/json')
            .withHeaders('Authorization', configData.HCPAuthorization)
            .expectStatus(200)
         console.log(response.json)

In this case, if the response does not match the expected status:200,
step: console.log(response.json) gets skipped

The above step helps in debugging, can we have a method exposed? like - print (response.json) within the pactum spec and this should help us with printing the response + debugging.

Matcher regex is not working as expected

Discussed in #98

Originally posted by captaincc423 December 1, 2021
I'm trying to get some matchers to work in expectJsonSnapshot and I'm running into a few problems:

  1. I'm trying to use a variable variable with a string in an "includes()", e.g. "id": includes(response.body.id) and it's not working. It always says it doesn't match.
  2. I'm using a regex and it appears that no matter what I put in it, it always matches. For instance, "createdAt": regex(/anything in here/), always passes.

Am I misusing these matchers?

Type Error when endpoint is not specified

Describe the bug
Running a pactum test with baseUrl and without an endpoint fails with type error.

To Reproduce
Steps to reproduce the behavior:

  1. Run Test
request.setBaseUrl(`<some-url>`);
await pactum.spec()
  1. See error
TypeError: Cannot read property 'startsWith' of undefined

Expected behavior
A clear and concise error would help.

Pactum Mock Server is format sensitive to Graphql Requests

Describe the bug
When making a graphql request to the mock server, a request made with a slightly different formatted gql body will cause the mock to believe it is a different defined interaction and return "Interaction Not Found"

To Reproduce
Steps to reproduce the behavior:

  1. Start Mock Server
  2. Define a GQL request interaction with a defined body
  3. Make GQL request with body defined from step 2 (returns expected mocked response)
  4. Slightly alter the formatting of the gql request
  5. Response is an "Interaction Not Found"

Expected behavior
The formatting of the request should not change the body of a gql request, and should return a mocked response if the request exists as a defined interaction

Screenshots
image
image
image

Software (please complete the following information):

  • OS: ubuntu 20.04
  • NodeJS Version 18.1

Save files from response to fs

Is your feature request related to a problem? Please describe.
It would be great to save files from response to file system and perform custom operations on it.

Describe the solution you'd like

await spec()
  .get('/some/file')
  .save('/path/')
  .expectStatus(200)

Describe alternatives you've considered

const fs = require('fs')
const { spec } = require('pactum');

const res = await spec().get('https://httpbin.org/image/png');

fs.writeFileSync('pig.png', res.buffer);

Feature Request: External JSON schema

Is your feature request related to a problem? Please describe.
I want to use external JSON schema provided in @exodus/schemasafe

Describe the solution you'd like
Please add an optional argument for external schemas to expectJsonSchema and jsonSchema methods.

Describe alternatives you've considered
Or maybe add a global setting for external schemas.

"Response does not contain a json array" error when it does. Same test passes in Cypress

Describe the bug
I have a test that keeps failing due to error "Response does not contain a json array" but the response does have this value. If I run the same test in Cypress it passes.

To Reproduce
Steps to reproduce the behavior:

  1. Run Test
  2. See error

Expected behavior
Test should pass.

Software (please complete the following information):

  • OS: Mac OS 12.5
  • NodeJS Version: 16.16.0

Additional context
AssertionError [ERR_ASSERTION]: Response does not contain a json array at 'wallets'

await spec()
      .get('/portfolio')
      .withHeaders('Authorization', `Bearer ${state.token}`)
      .expectStatus(200)
      .expectJsonLength('wallets', 1)
      .expectJsonLike({
        portfolios: {
          [state.portfolioId]: {
            wallets: [ 
              {
                address: "0x379F29c017eC33E145D1fe721971Cc1B50a31253",
                chain:   "ethereum"
              },
            ]
          }
        }
      })
  });

Response:

  "eXdTzDeajCSH4HogT5N9F": {
        "includes": [],
        "wallets": [
          {
            "chain": "ethereum",
            "address": "0x379F29c017eC33E145D1fe721971Cc1B50a31253"
          }
        ],
        "name": "api-patch-test",
        "portfolioId": "eXdTzDeajCSH4HogT5N9F",
        "createdAt": 1660213048605
      }

Same test that passes on Cypress:

cy.request({
        method: "GET",
        url: `${Cypress.env("portfolioAuthUrl")}/portfolio`,
        headers: {
          Authorization: `Bearer ${state.token}`,
        },
      }).then((resp) => {
        // redirect status code is 302
        expect(resp.status).to.eq(200);
        expect(resp.body.portfolios[state.portfolioId].wallets.length).eq(1);
        expect(
          resp.body.portfolios[state.portfolioId].wallets
        ).to.have.deep.members([
          {
            address: "0x379F29c017eC33E145D1fe721971Cc1B50a31253",
            chain: "ethereum",
          },
        ]);
      });
    });

Cannot get interaction with the specified ID

Describe the bug
mockserver.js
mock.addInteraction({ id:'first', request:{...}, response:{...}
getInteraction.test.js
`
test('should return "Jon" info',async()=>{
await pactum.spec()
.get('http://localhost:3000/ms/api/users/{userId}')
.withPathParams({"userId":"1"})
.expectStatus(200)
.expectJsonLike({
id: 1,
name:'Jon'
});

    expect(mock.getInteraction('first')).not.toBe(null);

});
`
results:

Interaction Not Found - first

  at msg.forEach.m (node_modules/pactum/src/adapters/logger.js:21:24)
      at Array.forEach (<anonymous>)
  at Object.warn (node_modules/pactum/src/adapters/logger.js:21:7)
  at Logger.warn (node_modules/pactum/src/plugins/logger.js:78:20)
  at Server.getInteraction (node_modules/pactum/src/models/server.js:77:11)
  at ids.forEach.id (node_modules/pactum/src/exports/mock.js:72:54)
      at Array.forEach (<anonymous>)

FAIL tests/mockServer/interactionWithPathParams.test.js
× should return "Jon" info (50 ms)

● should return "Jon" info

TypeError: Cannot read property 'callCount' of null

  15 |              });
  16 |
> 17 |      expect(mock.getInteraction('first')).not.toBe(null);
     |                  ^
  18 |
  19 | });
  20 |

  at interactions.forEach.interaction (node_modules/pactum/src/exports/mock.js:74:43)
      at Array.forEach (<anonymous>)

How do I get interaction with the given ID?

Mock Files

Is your feature request related to a problem? Please describe.
Mock server should be able to return files.

Describe the solution you'd like

{ request: {}, response: file: '' }

Interaction not exercised error is thrown on using contract testing

Describe the bug
Interaction not exercised error is thrown on using contract testing

To Reproduce

Have two made two endpoints up using mock server calls one for get: /api/inventory-service/products and for post: /api/order-service/orders
However flows are getting recorded in the server and can be witnessed under flows tab, but interaction not exercised error is thrown.

Expected behavior
Expected interaction to be listed in the pactumjs flow server

Screenshots
Screenshot 2022-06-07 at 6 48 21 PM

Screenshot 2022-06-07 at 6 48 46 PM

Software (please complete the following information):

  • OS: MAC
  • NodeJS Version: 16.3.1

Access to Request Options

Is your feature request related to a problem? Please describe.
Unable to access request information/options. I want to access the request options, to output a cURL request when a test fails. This is useful for reporting API bugs and being able to give the developers a command that they can execute to reproduce any issue.

Describe the solution you'd like
There could possibly be a getRequestOptions method on spec, this way anyone could pull that information and transform it into any request cURL, axios, etc.

Noticed that when a test fails, pactum outputs some request details, ideally, this is the information I would want in that method.

pactumjs

Describe alternatives you've considered
Tried getting that information from the response object, but I was not sure how to parse the request client, as a matter of fact, it didn't seem parsable (maybe I was missing something).

Additional context
I am open to working on this feature, and would only need some guidance as to where to start. Also, get feedback in terms of the best way of doing this. Maybe it's not a new method and can be done from like a handler?

Use headers from withHeaders while using multipart uploads

Describe the bug
Headers are not overriden when using withHeaders and withMultiPartFormData.

To Reproduce

var { spec } = require("pactum")

await spec()
    .get('https://httpbin.org/anything')
    .withMultiPartFormData('file', '')
    .withHeaders('content-type', 'application/json')
    .inspect()

Expected behavior

content-type header should be application/json but it still uses multipart/form-data

addDataFuncHandler should accept asynchronous function bodies

Is your feature request related to a problem? Please describe.
It's annoying that I can't use asynchronous functions in Data Function handlers

Describe the solution you'd like
I invision something like:

pactum.handler.addAsyncDataFuncHandler('MyDataFunction', async () => {
  const result = await someAsyncFunction(10);
  return result;
})

or

Refactor existing method to accept a boolean whose default value is false. But when set to true will pass the function to an async version of the handler instead (I don't love this approach).

pactum.handler.addDataFuncHandler('MyDataFunction', async () => {
  const result = await someAsyncFunction(10);
  return result;
}, true)

Data Management in Form Data

Is your feature request related to a problem? Please describe.
Able to use data-management features while using form-data.

Describe the solution you'd like

const { spec } = require('pactum');

it('should get random users', async () => {
  await spec()
    .get('https://some.me/api')
    .withMultiPartFormData({ '@DATA:TEMPLATE@': 'User' });
});

Matcher includes is not working in expectJsonSnapshot

Discussed in #98

Originally posted by captaincc423 December 1, 2021
I'm trying to get some matchers to work in expectJsonSnapshot and I'm running into a few problems:

  1. I'm trying to use a variable variable with a string in an "includes()", e.g. "id": includes(response.body.id) and it's not working. It always says it doesn't match.

Am I misusing these matchers?

Typescript GraphQLRequest['variables'] type as string when should be as object

Describe the bug
Trying to define a graphQL interaction in typescript project when I try to pass the variables key as object like I get a Typescript error expecting the type as string.

To Reproduce
Steps to reproduce the behavior:

  1. Create a typescript project
  2. Try to add interaction from spec().useInteraction() or mock.addInteraction() or any of the related methods.
  3. Define the request graphQL.variablesas object (like in tests)
  4. Typescript throws an error because no overload matchs.

Expected behavior
Typescript must work with graphQL.variables as Record<string, unknown>.

Screenshots
Captura de pantalla 2022-05-26 a las 12 19 09
Captura de pantalla 2022-05-26 a las 12 21 28

Software (please complete the following information):

  • OS: macOS Catalina 10.15.7
  • NodeJS Version 16.14.7
  • Typescript Version 4.3.5

Data Template - Removes

Is your feature request related to a problem? Please describe.
Support removes in data templates for negative scenarios.

Describe the solution you'd like

await spec()
  .post('<url>')
  .withJson({
    '@DATA:TEMPLATE@': 'User',
    '@REMOVES@': ['Surname']
  })
  .expectStatus(400)

Support Form Data in Mock Server

Is your feature request related to a problem? Please describe.
Support form data in mock server.

Describe the solution you'd like

mock.addInteraction({
  request: {
    method: 'POST',
    path: '/api/token',
    form: {
      'key': 'value'
    }
  },
  response: {
    status: 200
  }
})

Describe alternatives you've considered
Ignore the form data.

mock.addInteraction({
  strict: false,
  request: {
    method: 'POST',
    path: '/api/token'
  },
  response: {
    status: 200
  }
})

using colon as part of URL

Describe the bug
I tried to send a GET request with a URL, which contains a colon and it failed with error 401.
My request looks like

 const url=`https://${process.env.API_KEY}:${process.env.API_SECRET}@${process.env.URL}/v2/profiles`;
 spec().get(url)

I'm using API_KEY and API_SECRET for the basic authentication, that I provide as part of the URL.
Important to notice, that when I use axios.get(url); it works as required.
As workaround, I can use also Pactum, but with some changes:

const encode=Buffer.from(`${process.env.API_KEY}:${process.env.API_SECRET}`).toString('base64');
 const baseUrl=`https://${process.env.URL}/v2/profiles`;
 await pactum.spec().withHeaders({
         'Authorization': `Basic ${encode}`,
     })
         .get(baseUrl)
         .expectStatus(200);
 });

Expected behavior
could you please check the option to use the first command without basic authorization

Software (please complete the following information):

  • OS: macOS
  • NodeJS Version 14.15.1

Different behaviour between withCookies(cookies) and withHeaders('cookie', cookies)

Describe the bug
withCookies() method encodes the value before sending it, causing it not to work in some scenarios. withHeaders('cookie', cookie) doesn't.

withHeaders:
"cookie": "dwsid=M_ZChQR1JiTC3tzkLgdnlqMO8j--MFXD9r-bo-y1boZQnzjkfxiHXeWgPO2LytY9y6h0-Vykz99putNywubh5g=="

withCookies:
"cookie": "dwsid=M_ZChQR1JiTC3tzkLgdnlqMO8j--MFXD9r-bo-y1boZQnzjkfxiHXeWgPO2LytY9y6h0-Vykz99putNywubh5g%3D%3D"

Mind the %3D%3D at the end

To Reproduce
Steps to reproduce the behavior:

  1. Run a test sending a cookie value using withCookies()
  2. Inspect the request being sent

Expected behavior
I'm not sure if it should be encoded or not, but definitely, Salesforce Commerce Cloud platform doesn't like that, so maybe it should be configurable. Like I said, the behaviour is different if you use withHeaders('cookie'), so it's confusing

Software (please complete the following information):

  • OS: macOS 12.0.1
  • NodeJS Version: v14.17.6

Support sending and asserting cookies

Background

An HTTP cookie (web cookie, browser cookie) is a small piece of data that a server sends to the user's web browser. The browser may store it and send it back with later requests to the same server.

Learn more about cookies here.

Description

This issue focuses on sending cookies & asserting cookies in a friendly way with PactumJS.

Sending Cookies

Key-Value
withCookies('name', 'snow')
Object
withCookies({
  name: 'snow',
  'HttpOnly': null
})
Raw
withCookies('name=snow;HttpOnly')

Support multiple cookies.

await pactum.spec()
  .get('url')
  .withCookies('key1', 'value1')
  .withCookies('key2', 'value2')

Asserting Cookies

To support rich assertions, use json-like or json-match internally. I prefer json-match.

await pactum.spec()
  .get('url')
  .expectCookies('key1', 'value1');
Key-Value
expectCookies('name', 'snow')
Object
expectCookies({
  name: 'snow',
  'HttpOnly': null
})

expectStrictCookies({
  name: 'snow',
  'HttpOnly': null
})
Raw
expectCookies('name=snow;HttpOnly')

Note

Use lightweight cookie parsers like lightcookie or simple-cookie.

AC

  1. PactumJS should support sending & asserting cookies.
  2. Add withCookies, expectCookies & expectStrictCookies.
  3. Add tests

The mock server can only store the state for the first time using the Stores property

Describe the bug
The mock server has an interaction, two tests are written to access it, and stores can only hold data for the first time.

To Reproduce

  1. Environment
    "devDependencies": { "jest": "^27.3.1", "pactum": "^3.0.21" }
  2. Code
    // mockServer.js
mock.addInteraction({
   request:{
       method:'POST',
       path:'/api/users/new',
       body:like({
           id: 1,
           name:'tom',
           gender:'M',
           age:18
       })
   },     
   stores:{
       nid:'req.body.id',
       nname:'req.body.name',
       ngender:'req.body.gender',
       nage:'req.body.age'
   },
   response:{
       status:200,
       body:{
           id:'$S{nid}',
           name:'$S{nname}',
           gender:'$S{ngender}',
           age:'$S{nage}'
       }
   }
})
  1. Test
//setup
beforeAll(async()=>{
    stash.addDataTemplate({
        'User:new':{
            'id':4,
            'name':'kelly',
            'gender':'M',
            'age':23
        }
    });

    pactum.request.setBaseUrl('http://localhost:3000');

    //mock server start
    await pactum.mock.start(3000);

});

afterAll(async()=>{
    await pactum.mock.stop();
});

//test
test('should return post user info',async()=>{
    await pactum.spec()
        .post('/api/users/new')
        .withJson({
            '@DATA:TEMPLATE@':'User:new'
        })
        .expectStatus(200)
        .expectJsonMatch({
            id:like(1),
            name:'kelly',
            'age':23
        });
});

// test datatemplate: overides
////This library supports the overriding of specific values & extending the data template. 
test('should return age=-1 user info',async()=>{
    await pactum.spec()
        .post('/api/users/new')
        .withJson({
            '@DATA:TEMPLATE@':'User:new',
            '@OVERRIDES@':{
                'age':-1,
            }
        })
        .expectStatus(200)
        .expectJsonMatch({
            id:like(1),
            age:-1
        });
});
  1. result
    the second test failed.

Screenshots

FAIL __tests__/dataManagement/dataTemplate.test.js
  √ should return post user info (45 ms)
  × should return age=-1 user info (16 ms)

  ● should return age=-1 user info

    assert.fail(received, expected)
    
    Message:
      Json doesn't have value '-1' at '$.age' but found '23'


Request and response for the second test:
request:
 {
      "url": "http://localhost:3000/api/users/new",
      "method": "POST",
      "path": "/api/users/new",
      "body": {
        "id": 4,
        "name": "kelly",
        "gender": "M",
        "age": -1
      },
      "timeout": 3000
    }
response:
    {
      "statusCode": 200,
      "headers": {
        "content-type": "application/json",
        "date": "Mon, 01 Nov 2021 11:19:48 GMT",
        "connection": "close",
        "content-length": "45"
      },
      "body": {
        "id": 4,
        "name": "kelly",
        "gender": "M",
        "age": 23
      }
    }

Option to disable console logs.

Hi would be fine, to add option to disable logs during tests:

class Server 
          log.info(Mock server is listening on port ${config.mock.port}); //start method
          log.info(Mock server server stopped on port ${config.mock.port}); //stop method

current behaviour:

PASS  test/functional/example.spec.ts
  ● Console

    console.info
      Mock server is listening on port 9339

      at node_modules/pactum/src/exports/logger.js:69:37
          at Array.forEach (<anonymous>)

    console.info
      Mock server stopped on port 9339

      at node_modules/pactum/src/exports/logger.js:69:37
          at Array.forEach (<anonymous>)

Solutions:

  1. Change log error level from info to debug, then it could be omit by process.env.PACTUM_LOG_LEVEL
  2. Add another parameter to mock
    await pactum.mock.start(9339, {loggerEnabled: false});

Typescript errors?

Is it because of my Typescript version, 4.5.4...? Or perhaps my general naivety....?

OS: Windows 10
NPM: 7.24.1
Node: v16.10.0
Typescript: 4.5.4
Pactum: 3.1.1

When my tsconfig.js has "allowJs": true, adding a JS file that just requires pactum causes the below:

node_modules/pactum/src/exports/expect.d.ts:39:1 - error TS2309: An export assignment cannot be used in a module with other exported elements.
39 export = expect;
   ~~~~~~~~~~~~~~~~

node_modules/pactum/src/exports/reporter.d.ts:1:51 - error TS2440: Import declaration conflicts with local declaration of 'Interaction'.

1 import { InteractionRequest, InteractionResponse, Interaction } from './mock';
                                                    ~~~~~~~~~~~

node_modules/pactum/src/index.d.ts:5:25 - error TS2498: Module '"L:/src/foaf/node_modules/pactum/src/exports/expect"' uses 'export =' and cannot be used with 'export *'.

5 export * as expect from './exports/expect';
                          ~~~~~~~~~~~~~~~~~~

node_modules/pactum/src/index.d.ts:7:27 - error TS2307: Cannot find module './exports/matcher' or its corresponding type declarations.        

7 export * as matchers from './exports/matcher';
                            ~~~~~~~~~~~~~~~~~~~

node_modules/pactum/src/index.d.ts:15:25 - error TS2307: Cannot find module './exports/logger' or its corresponding type declarations.        

15 export * as logger from './exports/logger';
                           ~~~~~~~~~~~~~~~~~~

node_modules/pactum/src/models/Spec.d.ts:201:63 - error TS2314: Generic type 'Array<T>' requires 1 type argument(s).

201   withMultiPartFormData(key: string, value: string | Buffer | Array | ArrayBuffer, options?: FormData.AppendOptions): Spec;
                                                                  ~~~~~

node_modules/pactum/src/models/Spec.d.ts:398:19 - error TS2304: Cannot find name 'T'.

398   toss(): Promise<T>;
                      ~

Found 7 errors.

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "allowJs": true,
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": [
      "esnext",
    ],
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "typeRoots": [
      "jest-fetch-mock",
      "./node_modules/@types",
      "./src/types"
    ],
    "baseUrl": ".",
    "paths": {
      "testlib/*": [
        "./test/lib/*"
      ],
      "src/*": [
        "./src/*"
      ]
    }
  }
}

Setting "allowJs": false solves the problem.

Date Utils

Is your feature request related to a problem? Please describe.
It would be great to have a inbuilt date manipulating functions like momentjs or luxon

Describe the solution you'd like

const { date } = require('pactum');

date.now();
date.now().add({ days: 2});
date.now().minus({ hours: 2}).format('dd MM yyyy');

Eslint complains that test await a non-Promise value

Describe the bug
The simple example (see below) shows this error from eslint: Unexpected 'await' of a non-Promise (non-"Thenable") value. If I disable this error, typescript complains 'await' has no effect on the type of this expression.ts(80007)

The test then works without problems.

To Reproduce
Steps to reproduce the behavior:

  1. Copy the code to test.ts
  2. Install jest
  3. Prepare eslint configuration file (see below for mine)
  4. eslint -c config.json"
  5. See the error
  6. Now disable the error adding /* eslint-disable @typescript-eslint/await-thenable */
  7. Click on the … under await and see the typescript complain

Expected behavior
No complains.

Screenshots
If applicable, add screenshots to help explain your problem.

Software (please complete the following information):

  • OS: Windows
  • NodeJS Version 18.4.0
  • eslint 8.22.0
  • typescript 4.7.4
  • pactum 3.1.14

Additional context

The code:

import {spec} from "pactum";
const url = "http://localhost:8080/api";
describe("Configuration data", () => {
	test("should get a response with status code 200", async () => {
		await spec()
			.get(url)
			.expectStatus(200);
	});
});

The eslint configuration:

parser: '@typescript-eslint/parser'

plugins:
    - node
    - promise
    - import
    - '@typescript-eslint'
    - no-useless-assign
    - eslint-comments
    - dollar-sign
    - optimize-regex
    - const-case
    - jest
    - jest-formatting
    - unicorn
    - security
    - array-plural
    - no-unsafe-regex
    - deprecate
    - '@delagen/deprecation'
    - sonarjs
    - regexp
    - '@alasdair/max-len'
    - 'tsdoc'

extends:
    - 'eslint:recommended'
    - 'plugin:eslint-comments/recommended'
    - 'plugin:promise/recommended'
    - 'plugin:node/recommended'
    - 'plugin:import/errors'
    - 'plugin:import/warnings'
    - 'plugin:import/typescript'
    - 'plugin:@typescript-eslint/recommended'
    - 'plugin:@typescript-eslint/recommended-requiring-type-checking'
    - 'plugin:jquery/slim'
    - 'plugin:jquery/deprecated'
    - 'plugin:jest/recommended'
    - 'plugin:jest/style'
    - 'plugin:jest-formatting/recommended'
    - 'plugin:unicorn/recommended'
    - 'plugin:security/recommended'
    - 'plugin:sonarjs/recommended'
    - 'plugin:regexp/recommended'

parserOptions:
    ecmaVersion: 2021
    ecmaFeatures:
        impliedStrict: true
    project:
        - ./server/tsconfig.json
        - ./tests/tsconfig.json
        - ./ingest/tsconfig.json
        - ./common/tsconfig.json
    warnOnUnsupportedTypeScriptVersion: false

reportUnusedDisableDirectives: true

settings:
    import/parsers: {'@typescript-eslint/parser': [.ts, .tsx]}
    import/resolver: {typescript: {}}
    import/extensions: [.js, .ts]
    import/ignore: [node_modules]

env:
    es2021: true
    node: true
    jest: true
    jest/globals: true

rules:
    node/no-deprecated-api: error
    node/exports-style: [error, exports]
    node/no-unsupported-features/node-builtins: [error, {version: '>=16'}]
    node/no-unsupported-features/es-builtins: [error, {version: '>=16'}]
    node/no-unsupported-features/es-syntax: [off, {version: '>=16'}]
    node/no-missing-import:
        - off
        - allowModules: [marked]
          tryExtensions: [.js, .json]
    node/handle-callback-err: error
    node/global-require: error
    node/no-process-exit: off

    '@typescript-eslint/consistent-type-assertions': [warn, {assertionStyle: 'as'}]
    '@typescript-eslint/array-type': [warn, {default: array, readonly: array}]

    '@alasdair/max-len/max-len':
        - warn
        - code: 115
          ignoreTrailingComments: true
          ignoreUrls: true
          ignoreStrings: true
          ignoreTemplateLiterals: true
          ignoreRegExpLiterals: true

    no-dupe-class-members: off
    '@typescript-eslint/no-dupe-class-members': error
    no-buffer-constructor: error
    no-redeclare: [off, {builtinGlobals: true}]
    '@typescript-eslint/no-redeclare': [error, {builtinGlobals: true}]
    no-unused-vars: off
    '@typescript-eslint/no-unused-vars': error
    no-empty-function: off
    '@typescript-eslint/no-empty-function': warn
    no-useless-constructor: off
    '@typescript-eslint/no-useless-constructor': warn
    lines-between-class-members: off
    '@typescript-eslint/lines-between-class-members': off

    no-undef: error
    no-extend-native: error
    no-sequences: error
    no-new: error
    no-bitwise: error
    no-unsafe-negation: [warn, {enforceForOrderingRelations: true}]
    quotes: [off, double, {avoidEscape: true}]
    '@typescript-eslint/quotes': [warn, double, {avoidEscape: true}]
    eqeqeq: [error, always]
    strict: [error, global]
    no-loop-func: off
    '@typescript-eslint/no-loop-func': error
    no-unused-expressions: off
    '@typescript-eslint/no-unused-expressions': warn
    max-params: [warn, 4]
    max-len: [off, 113]
    space-before-function-paren: off
    '@typescript-eslint/space-before-function-paren':
        - error
        - anonymous: never
          named: never
          asyncArrow: always
    space-before-blocks: warn
    no-shadow: off
    '@typescript-eslint/no-shadow':
        - error
        - hoist: all
          builtinGlobals: true
          allow: [$, event, self]
    comma-spacing: off
    '@typescript-eslint/comma-spacing': [error, {before: false, after: true}]
    keyword-spacing: off
    '@typescript-eslint/keyword-spacing':
        - warn
        - before: true
          after: false
          overrides:
            else: {after: true}
            return: {after: true}
            try: {after: true}
            catch: {after: false}
            case: {after: true}
            const: {after: true}
            throw: {after: true}
            let: {after: true}
            do: {after: true}
            of: {after: true}
            as: {after: true}
            finally: {after: true}
            from: {after: true}
            import: {after: true}
            export: {after: true}
            default: {after: true}
    no-trailing-spaces: warn
    no-implicit-coercion: error
    id-length: [warn, {exceptions: [$, i, j, k, x, y, w, h, g]}]
    prefer-const: warn
    for-direction: error
    no-template-curly-in-string: error
    consistent-return: [error, {treatUndefinedAsUnspecified: false}]
    no-unmodified-loop-condition: error
    array-bracket-spacing: [warn, never]
    object-curly-spacing: off
    '@typescript-eslint/object-curly-spacing': ['warn']
    brace-style: off
    '@typescript-eslint/brace-style': [warn, stroustrup, {allowSingleLine: true}]
    spaced-comment: [warn, always, {markers: [':', '-', '+', '::', '/']}]
    no-var: error
    block-scoped-var: error
    yoda: error
    camelcase: [warn, {properties: never}]
    comma-dangle: [off, never]
    '@typescript-eslint/comma-dangle': [off, {arrays: only-multiline, objects: only-multiline}]
    max-depth: [warn, 8]
    arrow-parens: error
    no-confusing-arrow: [error, {allowParens: false}]
    dot-location: [error, property]
    no-else-return: error
    no-throw-literal: off
    '@typescript-eslint/no-throw-literal': error
    require-await: off
    '@typescript-eslint/require-await': error
    no-return-await: off
    '@typescript-eslint/return-await': error
    dot-notation: off
    '@typescript-eslint/dot-notation': off
    eol-last: [error, always]
    newline-per-chained-call: [error, {ignoreChainWithDepth: 3}]
    nonblock-statement-body-position: [warn, beside]
    space-infix-ops: off
    '@typescript-eslint/space-infix-ops': warn
    semi-spacing: error
    operator-assignment: [error, always]
    object-shorthand: [error, properties, {avoidQuotes: true}]
    no-process-exit: off
    no-negated-condition: warn
    no-constant-condition: [error, {checkLoops: false}]
    prefer-destructuring:
        - error
        - VariableDeclarator: {array: false, object: true}
          AssignmentExpression: {array: false, object: false}
        - enforceForRenamedProperties: false
    no-extra-parens: off
    '@typescript-eslint/no-extra-parens': [warn, functions]
    no-invalid-this: off
    '@typescript-eslint/no-invalid-this': [off, {capIsConstructor: false}]
    prefer-template: warn
    semi: off
    '@typescript-eslint/semi': [error, always]
    '@typescript-eslint/explicit-function-return-type': [warn, {allowExpressions: true}]
    '@typescript-eslint/method-signature-style': warn
    '@typescript-eslint/no-implicit-any-catch': warn
    '@typescript-eslint/prefer-includes': warn
    '@typescript-eslint/prefer-nullish-coalescing': warn
    '@typescript-eslint/prefer-optional-chain': warn
    '@typescript-eslint/no-base-to-string': warn
    '@typescript-eslint/non-nullable-type-assertion-style': warn
    '@typescript-eslint/no-unnecessary-boolean-literal-compare': warn
    '@typescript-eslint/prefer-readonly': warn
    '@typescript-eslint/prefer-readonly-parameter-types': off
    '@typescript-eslint/no-confusing-void-expression': [off, {ignoreArrowShorthand: true}]
    '@typescript-eslint/explicit-member-accessibility':
        - warn
        - accessibility: 'explicit'
          overrides:
            constructors: 'no-public'
            properties: 'explicit'
            parameterProperties: 'explicit'
            methods: 'no-public'
            accessors: 'no-public'
    no-multiple-empty-lines: [warn, {max: 2, maxEOF: 1}]
    prefer-arrow-callback: warn
    array-callback-return: [error, {allowImplicit: true}]
    init-declarations: off
    '@typescript-eslint/init-declarations': off
    func-call-spacing: off
    '@typescript-eslint/func-call-spacing': warn
    default-param-last: off
    '@typescript-eslint/default-param-last': warn

    optimize-regex/optimize-regex: off

    const-case/uppercase: off

    import/no-unresolved: off
    import/no-named-as-default: error
    import/no-named-as-default-member: error
    import/default: error
    import/no-deprecated: warn

    jest-formatting/padding-around-expect-groups: warn
    jest/no-disabled-tests: off

    array-plural/array-plural:
        - off
        - allows:
            - ignore
            - array
            - list
            - match
            - count
            - table

    unicorn/no-keyword-prefix: [warn, {checkProperties: false}]
    unicorn/prefer-array-some: warn
    unicorn/prefer-default-parameters: warn
    unicorn/prefer-array-index-of: warn
    unicorn/prefer-regexp-test: warn
    unicorn/consistent-destructuring: warn
    unicorn/prefer-string-starts-ends-with: warn
    '@typescript-eslint/prefer-string-starts-ends-with': warn
    unicorn/numeric-separators-style: [error, number: {onlyIfContainsSeparator: true, minimumDigits: 3}]
    unicorn/no-console-spaces: warn
    unicorn/prefer-string-replace-all: warn
    unicorn/prevent-abbreviations:
        - warn
        - replacements:
            len: false
            params: false
            doc: false
            pkg: false
            ctx: false
            i: false
            j: false
            idx: false
            args: false
            dir: false
          checkFilenames: false

    promise/catch-or-return: [error, {allowFinally: true}]

    dollar-sign/dollar-sign: [warn, ignoreProperties]
    no-useless-assign/no-useless-assign: warn

    deprecate/function: warn
    deprecate/member-expression: warn
    deprecate/import: warn
    '@delagen/deprecation/deprecation': warn

    no-unsafe-regex/no-unsafe-regex: error

    '@typescript-eslint/member-delimiter-style': warn
    tsdoc/syntax: warn

    no-loss-of-precision: off
    '@typescript-eslint/no-loss-of-precision': error
    no-magic-numbers: [off, {ignoreArrayIndexes: true, ignore: [0]}]
    '@typescript-eslint/no-magic-numbers': [off, {ignoreArrayIndexes: true, ignore: [0]}]
    promise/always-return: off
    linebreak-style: [off, windows]
    no-tabs: off
    one-var: [off, always]
    no-mixed-spaces-and-tabs: off
    no-multi-spaces: off
    no-plusplus: [off, {allowForLoopAfterthoughts: true}]
    no-console: off
    unicorn/filename-case: [off, {case: camelCase}]
    node/no-missing-require: off
    node/no-unpublished-require: off
    node/no-unpublished-import: off
    unicorn/throw-new-error: 0
    unicorn/new-for-builtins: 0
    unicorn/no-process-exit: 0
    unicorn/prefer-query-selector: 0
    unicorn/consistent-function-scoping: 0
    unicorn/no-for-loop: 0
    unicorn/no-useless-undefined: 0
    unicorn/no-array-reduce: 0
    security/detect-non-literal-fs-filename: 0
    security/detect-object-injection: 0
    unicorn/prefer-node-protocol: warn
    '@typescript-eslint/interface-name-prefix': 0
    indent: [off, tab]
    '@typescript-eslint/indent': [off, tab]
    no-use-before-define: off
    '@typescript-eslint/no-use-before-define': off
    '@typescript-eslint/no-non-null-assertion': off
    sonarjs/cognitive-complexity: [warn, 50]
    sonarjs/no-duplicate-string: [off, 6]
    sonarjs/elseif-without-else: 0
    sonarjs/no-nested-switch: 0
    unicorn/better-regex: 0
    unicorn/prefer-json-parse-buffer: 0

Support Global Expectations

Background

PactumJS supports global request settings which are applied to every request that is sent through this library. These global request settings can be overridden at spec level.

const { request } = require('pactum');

request.setBaseUrl();
request.setDefaultHeaders();

Description

In the similar terms, this library should support global expectations that are applied to all the responses received by this library.

Example

const { response } = require('pactum');

response.setDefaultExpectResponseTime();

Add default global expectations

  • setDefaultExpectResponseTime()
  • setDefaultExpectStatus()
  • setDefaultExpectHeader()

These expectations should be overridden at spec level as well.

const pactum = require('pactum');
const { response } = pactum;

response.setDefaultExpectResponseTime(1000);

it('should be a teapot', async () => {
  await pactum.spec()
    .get('http://httpbin.org/status/418')
    .expectStatus(418); 
  // default expectation of response time is applied here
});

it('should not be a teapot', async () => {
  await pactum.spec()
    .get('http://httpbin.org/status/200')
    .expectStatus(200)
    .expectResponseTime(500);
  // default expectation of response time is overridden here
});

AC

  1. PactumJS supports global expectations on status, headers & response time
  2. Add tests

Inspect() method does not work on running multiple test files.

inspect() method works only for single test case file

test files:
test/CreateHCF.js
test/CreateShifts.js
test/AssignShifts.js

Trigger command: mocha test/CreateHCF.js test/CreateShifts.js test/AssignShifts.js - it does not work in this case, request and response are not printed.

Trigger command:mocha test/CreateHCF.js - it works in this case, request and response are printed.

I cannot refer to the stored value

I want to store the access token to use this value later. I have :

const pactum = require('pactum');
const { Given, When, Then, Before } = require('@cucumber/cucumber');

Before(async () => {
    await pactum
      .spec()
      .post('/credentials')
      .withForm({
        username: email,
        password: passw,
      })
      .expectStatus(200)
      .stores('token', 'access_token');
})
    pactum.request.setDefaultHeaders('Authorization', 'Bearer $S{token}');

syntax '$S{token}' is a string, the next request is with "headers": {
"Authorization": "Bearer $S{token}"
},

what am I doing wrong?

Auth0 authentication

How can I use OAuth / ("Bearer " + token) authentication with Pactumjs, I can use basic username and password authentication just not find a way to use Auth0. Any help would be much appreciated.

Replace form-data with multi-part-lite

Background

Multipart requests combine one or more sets of data into a single body, separated by boundaries. You typically use these requests for file uploads and for transferring data of several types in a single request (for example, a file along with a JSON object).

Description

To support Multipart requests, PactumJS is currently using form-data which is inspired from XMLHttpRequest-2 FormData Interface. form-data internally uses mimes-type package to auto set content-type for file uploads. The overall size of this package is above 300 kB.

PactumJS library strives to be as light as possible. The current size of PactumJS is above 800 kB. (37% of it is from form-data).

Solution

Instead of form-data, check if we can use multi-part-lite. This package size is just 20 kB.

  • Check if this package can replace form-data.
  • If it can replace, what additional data should be provided for performing requests.
  • Test all scenarios.

AC

  1. Use multi-part-lite package.

Support more HTTP methods

Background

PactumJS supports the following HTTP methods to perform a HTTP request.

  • GET
  • POST
  • DELETE
  • PATCH
  • PUT
  • HEAD

Description

This issue focuses on expanding the supported list of HTTP methods to TRACE & OPTIONS.

await pactum.spec()
  .trace('url')
  .expectStatus(200);
await pactum.spec()
  .options('url')
  .expectStatus(200);

Along with it, if a new HTTP method is available in the future then the users of this library should not be blocked by it. So the HTTP methods should be expandable. Introduce two new request making methods - withMethod & withPath that takes HTTP method & url as parameters.

await pactum.spec()
  .withMethod('CONNECT')
  .withPath('url')
  .expectStatus(200);

AC

  1. Support TRACE & OPTIONS HTTP methods.
  2. Support custom HTTP methods.
  3. Add appropriate tests in request.spec.js under test/component folder.

Cannot refer to the stored value in request defaults

Discussed in #130

Originally posted by martynaol March 28, 2022
I want to store the access token to use this value later. I have :

const pactum = require('pactum');
const { Given, When, Then, Before } = require('@cucumber/cucumber');

Before(async () => {
    await pactum
      .spec()
      .post('/credentials')
      .withForm({
        username: email,
        password: passw,
      })
      .expectStatus(200)
      .stores('token', 'access_token');
})
    pactum.request.setDefaultHeaders('Authorization', 'Bearer $S{token}');

syntax '$S{token}' is a string, the next request is with "headers": {
"Authorization": "Bearer $S{token}"
},

what am I doing wrong?

require error in documentation

Describe the bug
On your documentation website there is a reference to install cucumber using:
npm install @cucumber/cucumber -g
However, when you require the module later, you use require('cucumber'), which generates and error.

To Reproduce
Steps to reproduce the behavior:
Check the documentation site, and I just noticed it is the same in the readme here (on github).

Expected behavior
The line should read const { Given, When, Then, Before } = require('@cucumber/cucumber');

Screenshots
image

Software (please complete the following information):
N/A

Additional context
Add any other context about the problem here.

'await' has not effect on this type of expression

Hi,

I'm trying to setup PactumJS with a Hapi project. I've written one small test test but I'm getting the following Typescript error/suggestion:

image

Removing 'await' causes the tests to pass all the time (even when they should fail if I change the expected result). So, await is clearly required but the linter is suggesting otherwise.

Steps to reproduce the behavior:

  1. Setup project with following dependencies:
"dependencies": {
    "@hapi/hapi": "^20.2.1"
  },
  "devDependencies": {
    "@types/chai": "^4.3.0",
    "@types/hapi__hapi": "^20.0.10",
    "@types/mocha": "^9.1.0",
    "@types/node": "^17.0.15",
    "chai": "^4.3.6",
    "mocha": "^9.2.0",
    "nodemon": "^2.0.15",
    "npm-run-all": "^4.1.5",
    "pactum": "^3.1.3",
    "ts-node": "^10.5.0",
    "typescript": "^4.5.5"
  }

and following tsconfig:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./lib",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
  },
  "exclude": [
    "test"
  ]
}
  1. Create test file like so:
import pactum from 'pactum';

it('should return 200', async () => {
  await pactum.spec()
    .get('http://localhost:4000/')
    .expectStatus(200);
});

The await keyword does not throw an error but makes an incorrect suggestion that the await keyword is not required but it definitely is.

Expected behaviour
No Typescript suggestion to remove something that is required.

Software (please complete the following information):

  • OS: Mac OSX 11.6.1
  • NodeJS Version: 16.14.0

This isn't a massive issue, I'm just worried that I / other devs will remove the await keyword and assume the tests are working as expected.

Requests signed with certificates(PEM/Key)

Is your feature request related to a problem? Please describe.
i would like to send some request signed with certificates(PEM/KEY). but i cant passed a the certificate.

Describe the solution you'd like
i would like to passe a certificate via file or string, and it send my request.

Cookie jar feature

Is your feature request related to a problem? Please describe.
If you want to reuse the cookies from a previous request (or across multiple ones), you need to extract the cookies manually from the "set-cookie" from the first request (and from any request that might add a new cookie), and send it along with every subsequent request.

Describe the solution you'd like
It would be nice to be able to do something like:

const cookieJar = require('pactum').cookieJar();

await pactum.spec().get('/...').withCookieJar(cookieJar).expect(...);
await pactum.spec().post('/...').withCookieJar(cookieJar).expect(...);
await pactum.spec().post('/...').withCookieJar(cookieJar).expect(...);

cookieJar should also have some utility methods to getCookie/setCookie.

Maybe it also could be done automatically with a new setting pactum.request.setUseCookieJar(true); that will make all requests made in the same test to use the same cookie jar. Not sure if it's even possible, but it would be nice :) You could override it for a specific request sending a new cookie jar, for instance.

Thanks! I hope it's not too much asking 🙏

Option to automatically set content-length

Is your feature request related to a problem? Please describe.
After a number of hours, I realised my tests POSTing JSON are failing in Pactum because no content-length header is being sent.

Describe the solution you'd like
Either .withContentLength or some kind of default. Why would someone not want content-length set automatically if JSON encoding is done automatically?

Describe alternatives you've considered
.withBody(someVar).withHeaders({'content-length': JSON.stringify(someVar).length})

Additional context
I feel like I must have missed something in the docs.

Retry on Expectation Failures

Background

PactumJS supports retry mechanism where an user can define when to retry (strategy), how many times to retry & the delay between retries.

await pactum.spec()
  .get('/some/async/operation')
  .retry({
    count: 2,
    delay: 2000,
    strategy: ({res}) => { return res.statusCode === 200 }
  })
  .expectStatus(200);

Description

Enhance the retry mechanism to retry when there are expectation failures.

Retry on expectations failure

In the below test, if the endpoint returns a status code other than 202, PactumJS should retry the API request.

await pactum.spec()
  .get('/api/endpoint')
  .expectStatus(202)
  .retry();

The retry logic should apply for other expectation failures as well.

Support multiple options in retry() method

Retry with default options

retry() 

Retry with a custom retry count

retry(3); 

Retry with a custom retry count & custom delay

retry(3, 5000); 

AC

  1. PactumJS should retry on expectation failures.
  2. Add tests.

How to test cookie session?

I am using express-session when I signin I generate a cookie session, is there any example on how to store this cookie for future tests where you need to be signed in?

Custom expect handler does not print request and response when the assertion fails.

Describe the bug
Most expect methods print the request and response when the assertion fails.
However, the custom expect handler does not show these objects.

To Reproduce
Steps to reproduce the behavior:

const spec = pactum.spec();
spec.get(url);
await spec.toss();
await spec.response().to.have._((ctx) => {
  assert.fail('error');
});

Expected behavior
It should show the request and response as other methods do.

Software (please complete the following information):

  • OS: Linux
  • NodeJS: 16.1
  • Pactum: 3.0.19

Support cache mock interactions

Is your feature request related to a problem? Please describe.
During component testing, we use useInteraction to have expectations on the external calls. Same expectations are applied for multiple specs. Some micro-services will cache the response and that makes component tests to remove the cached response from cache server.

Describe the solution you'd like
useInteraction supports a new boolean property called cache will have the expectation exercised only once in the entire component testing cycle.

Describe alternatives you've considered
May be expectations would support the call count to be either 0 or 1.

Support adding multiple interactions from Interaction Handlers

Background

Interaction handlers are a way to reuse interactions across your test cases. With the current setup, we can only return one interaction in the callback function.

const { addInteractionHandler } = require('pactum').handler;

addInteractionHandler('get users', () => {
  return {
    request: {
      method: 'GET',
      path: '/api/users'
    },
    response: {
      status: 200
    }
  }
}); 

Description

Enhance the addInteractionHandler function to return multiple interactions.

const { addInteractionHandler } = require('pactum').handler;

addInteractionHandler('get users with id 1 & 2', () => {
  return [
  {
    request: {
      method: 'GET',
      path: '/api/users/1'
    },
    response: {
      status: 200,
      body: { "id": 1 }
    }
  },
  {
    request: {
      method: 'GET',
      path: '/api/users/2'
    },
    response: {
      status: 200,
      body: { "id": 2 }
    }
  }];
}); 

Potential Changes

AC

  1. addInteractionHandler function should support multiple interactions
  2. Add tests

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.