Git Product home page Git Product logo

superdeno's Issues

Re-introduce multiple cookie headers in test

It appears the cookie setting code in Opine has regressed cmorten/opine#117 this means we cannot test that setting multiple cookies works as part of the superdeno test suite.

This issue is to track the resolution of the Opine issue and then re-introduce said commented out tests once it is resolved.

[CHORE] Use Deno's inspect instead of one from NPM

Issue

I noticed that an NPM library was being used to provide node's util functionality, which brings in a total of 16 NPM packages. (22 vs 6 going by the badges that were just added ๐Ÿ˜„ )

It seems like this import is easily avoided by leveraging Deno's std library.

Details

After searching the codebase I determined that the util import from NPM was only being used to polyfill util.inspect() for error messages. I've prepared a patchset that removes the util NPM package in favor of Deno.inspect: main...danopia:use-deno-inspect

Going by the test suite, the only difference I observed was that Deno.inspect uses different quoting rules than the util.inspect from NPM. So I had to adjust some quotes in the test suite. I suppose it's possible that consumers are testing for exact messages too but overall the change doesn't seem very disruptive to me.

I also have a mutually exclusive patchset that uses Deno's /std/node polyfile for util.inspect but it doesn't seem to give anything over using Deno.inspect directly. Here it is anyway: main...danopia:use-std-util

I'm prepared to open a PR with either patchset if this looks agreeable.

Here's the before / after of the package dependency tree:

Screenshot 2021-02-10 at 20 21 54
Screenshot 2021-02-10 at 21 00 57

Can this be used with `express`?

Issue

Really a question, but with Opine being in maintenance mode, I tried the recommendation of using express on Deno.

I tried using superdeno to test routes, but when I run the test, the tests fails and then just hangs the process.

Steps to reproduce:

Run this using deno test:

// @deno-types="npm:@types/express@^4.17"
import express from "npm:[email protected]";
import { superdeno } from "https://deno.land/x/[email protected]/mod.ts";

const app = express();

app.get("/", (_req, res) => res.json("hello express"));

Deno.test("mod", async () => {
  await superdeno(app).get("/").expect("hello express").end();
});

I also tried using npm:supertest but that doesn't seem to work with npm:express on Deno either ๐Ÿคท .

I guess am not sure what the expectation is (pun unintended) for testing express apps on top of deno. Should I be spinning the app up and down in tests, and then manually calling with fetch?

Setup:

deno 1.31.1 (release, x86_64-unknown-linux-gnu)
v8 11.0.226.13
typescript 4.9.4

SuperDeno Version: 4.8.0

Details

Besides Opine being in maintenance mode, the hard impetus for trying out express on Deno was because my tests started to fail using superdeno and opine together. The tests seem to run, but then an error is thrown after each file is executed by Deno:

error: TypeError: core.runMicrotasks is not a function
    core.runMicrotasks();
         ^
    at processTicksAndRejections (https://deno.land/[email protected]/node/_next_tick.ts:62:10)
    at https://deno.land/[email protected]/node/process.ts:312:7

With some digging, it seems that opine is importing theses deps from std, and those deps are using an api removed in later versions of deno, which is causing an error to be thrown on beforeunload.

Don't know if this should be an issue on opine, but it seems opine cannot be used inside of Deno.test.

[BUG] res.header vs res.headers typescript issues

Issue

Setup:

  • Deno Version: deno 1.5.4 (bc79d55, release, x86_64-apple-darwin)
  • v8 Version: v8 8.8.278.2
  • Typescript Version: typescript 4.0.5
  • SuperDeno Version: 2.4.0

Header(s) properties on res object parameter in .expect() seem off.

Details

In the latest version (and previous ones!) there appears to be res.header and res.headers properties on the res object provided to the callback of the .expect() API.

Both appear to have issues with the typings, resulting in needing (res as any) like workarounds.

I believe the bug lies in the line:

type ExpectChecker = (res: Response) => any;

Which should read:

type ExpectChecker = (res: IResponse) => any;

In order to use the superagent response object, and not the Deno Response object. The whole thing needs a look at though as the real shape of the res object doesn't quite match the IReponse object either... so this should be fixed up as well!


Ideally it should conform to, and be typed as, either:

Or... a combination of the above if there is no / sensible overlaps in expected types for named properties.

The Response object has properties:

{
  headers: Headers;
  ok: boolean;
  redirected: boolean;
  status: number;
  statusText: string;
  trailer: Promise<Headers>;
  type: ResponseType;
  url: string;
  body: ReadableStream<Uint8Array> | null;
  bodyUsed: boolean;
}

And the superagent response looks like:

{
  text: string;
  body: string;
  header: { [key: string]: string };
  type: string;
  charset: string;
  status: number;
  type: number;
  info: number;
  ok: number;
  clientError: number;
  serverError: number;
  error: number;
  accepted: number;
  noContent: number;
  badRequest: number;
  unauthorized: number;
  notAcceptable: number;
  notFound: number;
  forbidden: number;
  ...
}

`HandlerLike` signature doesn't match actual `server.Handler`. Missing `connInfo`

HandlerLike signature doesn't match actual server.Handler. Missing connInfo

Setup:

  • Deno Version: deno 1.20.6
  • v8 Version: v8 10.0.139.6
  • Typescript Version: 4.6.2
  • SuperDeno Version: 4.8

Details

The actual server.Handler takes a second argument called connInfo that contains information for the http request. See here and quoted:

export type Handler = (
  request: Request,
  connInfo: ConnInfo,
) => Response | Promise<Response>;

But RequestHandlerLike defined in this project doesn't match that. And when actually testing the handler, this project calls the handler with return await app(request); with the connInfo argument missing.

Question: testing if header doesn't exist

Issue

Setup:

deno 1.12.0 (release, x86_64-unknown-linux-gnu)
v8 9.2.230.14
typescript 4.3.2

  • SuperDeno Version: 4.4.0

Details

At the moment this:

import { superdeno } from 'https://deno.land/x/[email protected]/mod.ts'
import { App } from 'https://deno.land/x/tinyhttp/mod.ts'
import { describe, it, expect, run } from 'https://deno.land/x/[email protected]/mod.ts'

describe('App settings', () => {
it('when disabled should not send anything', async () => {

const app = new App({ settings: { xPoweredBy: false } })

    app.use((_req, res) => void res.send('hi'))

    const request = superdeno(app.attach)

    await request.get('/').expect('X-Powered-By', null)

})
})

run()

results in this:

Error: expected "X-Powered-By" header field
    at Test.#assertHeader (https://deno.land/x/[email protected]/src/test.ts:562:14)
    at Test.#assertFunction (https://deno.land/x/[email protected]/src/test.ts:617:13)
    at Test.#assert (https://deno.land/x/[email protected]/src/test.ts:480:35)
    at https://deno.land/x/[email protected]/src/test.ts:455:23
    at async close (https://deno.land/x/[email protected]/src/close.ts:48:46)

failures:

        App settings > xPoweredBy > when disabled should not send anything

test result: FAILED. 101 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (2657ms)

What's the proper way to test for absence of a header?

How does superdeno intercept redirects?

Is there a way to test for redirects?

When I do something along the lines of:

    await superdeno(testApiUrl)
    .post('/api/to/endpoint/that/triggers/a/redirect')
    .expect((response, error) => {
       assertEquals(response.status, 302);
        assertEquals(response.headers.includes('location: http://google.com/'), true);
    });

This errors out and insists that the final state is 200, not 302.

I've noticed that the response.headers capture those of the final destination page rather than the actual API redirect state. Is there a way to go one step before the final destination URL?

I have also tried

    await superdeno(testApiUrl)
    .post('/api/to/endpoint/that/triggers/a/redirect')
    .expect(302)
    .expect((response, error) => {
        assertEquals(response.headers.includes('location: http://google.com/'), true);
    });

but to no effect.

Thanks heaps (again great library).

Couldn't make `multipart/form-data` requests with oak.

Issue

Setup:

  • Deno Version: 1.15.3 (release, x86_64-apple-darwin)
  • v8 Version: 9.5.172.19
  • Typescript Version: 4.4.2
  • SuperDeno Version: 4.6.0
  • Oak Version: v9.0.1

Details

I'm using oak server. I tried the following code to test a multipart/form-data endpoint, didn't seem to work.
Both superoak and superdeno seemed not to work properly.

await req
    .post(url)
    .field('form_key', 'form_value')
    .attach('form_key2', 'path_to_file', 'filename');

I can only receive an empty body text/plain request.
I've also tried using superagent with jspm. Still not working. Maybe its because nodejs package having some bugs on deno by jspm.

Getting async leaking error when testing opine v2

Issue: Getting async leaking error when testing opine v2

Setup:

  • Deno Version: 1.16.4
  • v8 Version: 9.7.16.15
  • Typescript Version: 4.4.2
  • SuperDeno Version: 4.7.1

Details

When running a simple test with [email protected] Deno Test Assertions return AssertionError: Test case is leaking async ops. - there are no async's in the example code which is pasted below.

server.ts

import { opine } from 'https://deno.land/x/[email protected]/mod.ts'


export function build() {

 const app = opine();

  app.get('/', function (req, res) {
    res.send('Hello World')
  })

  return app
}

test.ts

import {superdeno} from 'https://deno.land/x/[email protected]/mod.ts'
import { build } from './server.ts'
const app = build()

Deno.test('hello world', () => {
  superdeno(app)
    .get('/')
    .expect(200)
})

Run

deno test -A --unstable test.ts

Output

running 1 test from file:///workspace/deno-playground/test.ts
test hello world ... FAILED (12ms)

failures:

hello world
AssertionError: Test case is leaking async ops.
Before:
  - dispatched: 0
  - completed: 0
After:
  - dispatched: 2
  - completed: 1
Ops:
  op_net_accept:
    Before:
      - dispatched: 0
      - completed: 0
    After:
      - dispatched: 1
      - completed: 0

Make sure to await all promises returned from Deno APIs before
finishing test case.
    at assert (deno:runtime/js/06_util.js:41:13)
    at asyncOpSanitizer (deno:runtime/js/40_testing.js:128:7)
    at async resourceSanitizer (deno:runtime/js/40_testing.js:144:7)
    at async Object.exitSanitizer [as fn] (deno:runtime/js/40_testing.js:176:9)
    at async runTest (deno:runtime/js/40_testing.js:381:7)
    at async Object.runTests (deno:runtime/js/40_testing.js:494:22)

failures:

        hello world

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (147ms)

error: Test failed

Error: Request has been terminated for native http server

Issue

Setup:

  • Deno Version: 1.15.2
  • v8 Version: 9.5.172.19
  • Typescript Version: 4.4.2
  • SuperDeno Version: 4.6.0

Details

import { superdeno } from 'https://deno.land/x/[email protected]/mod.ts'
import { it, run } from 'https://deno.land/x/[email protected]/mod.ts'
import { App } from 'https://denopkg.com/deno-libs/tinyhttp@new-std-http/mod.ts'

it('simple test', async () => {
  const app = new App()

  const request = superdeno(app._server!) // or `app.handler`, it doesn't work either way

  await request.get('/').expect(404, 'Not Found')
})

run()

Error message:

Error: Request has been terminated
Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.
    at Test.Request.crossDomainError (https://jspm.dev/npm:[email protected]!cjs:674:13)
    at XMLHttpRequestSham.xhr.onreadystatechange (https://jspm.dev/npm:[email protected]!cjs:777:19)
    at XMLHttpRequestSham.xhrReceive (https://deno.land/x/[email protected]/src/xhrSham.js:115:29)
    at https://deno.land/x/[email protected]/src/xhrSham.js:62:21
    at Object.xhr.onreadystatechange (https://deno.land/x/[email protected]/src/xhrSham.js:211:7)
    at XMLHttpRequestSham.xhrSend (https://deno.land/x/[email protected]/src/xhrSham.js:335:20)
    at async XMLHttpRequestSham.send (https://deno.land/x/[email protected]/src/xhrSham.js:61:7)

Support new [email protected]/http API

Issue

Setup:

  • Deno Version: 1.14.0
  • v8 Version: 9.4.146.15
  • Typescript Version: 4.4.2
  • SuperDeno Version: 4.4

Details

With new std/http superdeno doesn't work anymore. superdeno should support new API and deprecate the old one

Release 1.5 causing an error on download

Issue

image

Setup:

  • Deno Version: 1.1.2
  • v8 Version: 8.5.216
  • Typescript Version: 3.9.2
  • SuperDeno Version: 1.5.0

I got the error in the screenshot above on first run (I upgraded from 1.2.1 to 1.5.0). There are actually two (first being a warning re the module implicitly referencing Deno master) and the second being the main blocker:

error: TS2305 [ERROR]: Module '"./_utils"' has no exported member 'normalizeEncoding'.
import { notImplemented, normalizeEncoding } from "./_utils.ts";

Submitting a payload with a get request results in request termination error

image

Issue

Submitting a payload with a get request results in request termination error

Setup:

  • Deno Version: 1.1.3
  • v8 Version: 8.5.216
  • Typescript Version: 3.9.2
  • SuperDeno Version: 1.5.1

Please replace this line with a short description of the issue.

Details

        await superdeno("http://localhost:8080/api")
        .get("/endpoint/")
        .send({
            "name": "asset X",
            "thingType": "truck-asset-type"
        })
       .expect(200);

results in the error above. It seems like superdeno doesn't support sending payloads with a get request?

Submitting a payload with a GET request is appropriate in this case as there'll be no side effect to the API. The body is essentially a DSL to help filter the response.

Attach is possibly not sending the actual file to the app

Issue

Setup:

  • Deno Version: 1.18.0
  • v8 Version: 9.8.177.6
  • Typescript Version: 4.5.2
  • SuperDeno Version: 4.8.0

Details

When using the .attach method on a request.post the body.value.read(), appears to be returning undefined for files. I am not sure what the reason for this might be, I have created a test-case showcasing the problem and can create a PR for the same if required. test case file

SuperDeno does not process headers in the response from Opine

SuperDeno does not process headers from Opine

Setup:

  • Deno Version: deno 1.9.1 (release, x86_64-unknown-linux-gnu)
  • v8 Version: 9.1.269.5
  • Typescript Version: 4.2.2
  • SuperDeno Version: 4.1.0

Details

When: testing the following Opine handler:

handler: (req: Request, res: Response, next: NextFunction) => {
  console.log(res.headers) // shows:  "x-powered-by": "Opine"
  res.json({ data: 'ok' })
}

I want the following test:

  await superdeno(app)
    .get('/')
    .expect(200)
    .then((response) => {
      console.log(response.headers)
    })

to return:

{
   "content-type": "application/json",
   "x-powered-by": "Opine"
}

instead of
{ "content-type": undefined }.

Full response:

Response {
  req: Test {
    _query: [],
    method: "GET",
    url: "http://127.0.0.1:38097/",
    header: {},
    _header: {},
    _callbacks: { "$end": [ [Function] ], "$abort": [ [Function] ] },
    app: [Function: app] {
      emit: [Function],
      ... (other methods)
      request: ServerRequest { app: [Circular] },
      response: Response { app: [Circular] },
      cache: {},
      engines: {},
      settings: {
        "x-powered-by": true,
        etag: "weak",
        "etag fn": [Function: generateETag],
        "query parser": "extended",
        "query parser fn": [Function: parseExtendedQueryString],
        "subdomain offset": 2,
        "trust proxy": false,
        "trust proxy fn": [Function: trustNone],
        view: [Function: View],
        views: "<my dir>/views",
        "jsonp callback name": "callback",
        "view cache": true
      },
      locals: { settings: [Object] },
      mountpath: "/",
      _router: [Function: router] {
        params: [Object],
        _params: [Array],
        caseSensitive: false,
        mergeParams: undefined,
        strict: false,
        stack: [Array]
      }
    },
    _maxRedirects: 0,
    _endCalled: true,
    _callback: [Function],
    xhr: XMLHttpRequestSham {
      id: "1",
      origin: "http://127.0.0.1:38097",
      onreadystatechange: [Function],
      readyState: 4,
      responseText: '{"data":"ok"}',
      responseType: "",
      response: '{"data":"ok"}',
      status: 200,
      statusCode: 200,
      statusText: "OK",
      aborted: false,
      options: {
        requestHeaders: [Object],
        method: "GET",
        url: "http://127.0.0.1:38097/",
        username: undefined,
        password: undefined,
        requestBody: null
      },
      controller: AbortController {},
      getAllResponseHeaders: [Function],
      getResponseHeader: [Function]
    },
    _fullfilledPromise: Promise { [Circular] }
  },
  xhr: XMLHttpRequestSham {
    id: "1",
    origin: "http://127.0.0.1:38097",
    onreadystatechange: [Function],
    readyState: 4,
    responseText: '{"data":"ok"}',
    responseType: "",
    response: '{"data":"ok"}',
    status: 200,
    statusCode: 200,
    statusText: "OK",
    aborted: false,
    options: {
      requestHeaders: {},
      method: "GET",
      url: "http://127.0.0.1:38097/",
      username: undefined,
      password: undefined,
      requestBody: null
    },
    controller: AbortController {},
    getAllResponseHeaders: [Function],
    getResponseHeader: [Function]
  },
  text: '{"data":"ok"}',
  statusText: "OK",
  statusCode: 200,
  status: 200,
  statusType: 2,
  info: false,
  ok: true,
  redirect: false,
  clientError: false,
  serverError: false,
  error: false,
  created: false,
  accepted: false,
  noContent: false,
  badRequest: false,
  unauthorized: false,
  notAcceptable: false,
  forbidden: false,
  notFound: false,
  unprocessableEntity: false,
  headers: { "content-type": undefined },
  header: { "content-type": undefined },
  type: "",
  links: {},
  body: null
}

4xx and 5xx response missing body and text

Issue

Setup:

  • deno 1.0.5
  • v8 8.4.300
  • typescript 3.9.2

When server response 4xx or 5xx, superdeno's response doesn't have body or text, so there is no way to test the HTTP body part.

import {
  Application,
} from "https://deno.land/x/ako/mod.ts";
import { superdeno } from "https://deno.land/x/[email protected]/mod.ts";
import { describe, it } from "https://deno.land/x/[email protected]/test/utils.ts";

describe("ctx.onerror(err)", () => {
  it("should respond", async () => {
    const app = new Application();

    app.use((ctx, next) => {
      ctx.body = "something else";

      ctx.throw(418, "boom");
    });

    const res = await superdeno(app.listen())
      .head("/")
      .expect(418)
      .expect("Content-Length", "4")
      .expect("Content-Type", "text/plain; charset=utf-8");
    console.log(res);

    // res.body is null
    // res.text is null
  });
});

curl:

curl -i  127.0.0.1:5000
HTTP/1.1 418 I'm a teapot
content-length: 4
content-type: text/plain; charset=utf-8

boom% 

4xx response body is null

Issue

Setup:

  • deno 1.0.5
  • v8 8.4.300
  • typescript 3.9.2

4xx response body is null, even though there is a response body.

Details

I want to test the custom response body, but there is nothing in res body.

By the way, when responding 4xx, superdeno also throws error which is different from supertest, which is annoying.

Expected "Hello World" response body, got "HTTP/1.1 20"

Issue

I get this error when I'm asserting a response body. For some reason it asserts with a raw HTTP response:

res.format(obj) > should send text by default
Error: expected "Hello World" response body, got "HTTP/1.1 20"
    at error (https://deno.land/x/[email protected]/src/test.ts:637:15)
    at Test.#assertBody (https://deno.land/x/[email protected]/src/test.ts:535:16)
    at Test.#assertFunction (https://deno.land/x/[email protected]/src/test.ts:617:13)
    at Test.#assert (https://deno.land/x/[email protected]/src/test.ts:480:35)
    at https://deno.land/x/[email protected]/src/test.ts:455:23
    at async close (https://deno.land/x/[email protected]/src/close.ts:48:46)

failures:

        res.format(obj) > should send text by default

test result: FAILED. 121 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (4184ms)

Setup:

deno 1.12.0 (release, x86_64-unknown-linux-gnu)
v8 9.2.230.14
typescript 4.3.2

  • SuperDeno Version: 4.4.0

Details

import { describe, it, run } from 'https://deno.land/x/[email protected]/mod.ts'
import { App, Handler, AppConstructor, Request, Response } from 'https://deno.land/x/tinyhttp/mod.ts'

const runServer = (h: Handler) => {
  const app = new App()

  app.use(h)

  const request = superdeno(app.attach)

  return request
}


it('should send text by default', async () => {
    const request = runServer((req, res) => {
      formatResponse(req, res, () => {})({
        text: (req: Request) => req.respond({ body: `Hello World` })
      }).end()
    })

    await request.get('/').expect(200).expect('Hello World')
  })

[BUG] Error thrown on empty body responses (e.g. 304)

Issue

Setup:

  • Deno Version: 1.3.1
  • v8 Version: 8.6.334
  • Typescript Version: 3.9.7
  • SuperDeno Version: 2.2.0

Seeing 304 Not Modified responses with empty (no) body result in an error thrown by SuperDeno.

error: Uncaught Error: Request has been terminated
Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.
    at Test.Request.crossDomainError (https://dev.jspm.io/npm:[email protected]/lib/client.dew.js:680:15)
    at XMLHttpRequestSham.xhr.onreadystatechange (https://dev.jspm.io/npm:[email protected]/lib/client.dew.js:783:21)
    at XMLHttpRequestSham.xhrReceive (https://deno.land/x/[email protected]/src/xhrSham.js:105:29)
    at XMLHttpRequestSham.send (https://deno.land/x/[email protected]/src/xhrSham.js:64:12)

Details

Adding some logs in the xhrSham we can see that (xhrSham.js:293:34):

...

window._xhrSham.promises[self.id] = fetch(options.url, {
  method: options.method,
  headers: options.requestHeaders,
  body,
  signal: this.controller.signal,
  mode: "cors",
});

// Wait on the response, and then read the buffer.
response = await window._xhrSham.promises[self.id];

...

const buf = await response.arrayBuffer();
parsedResponse = decoder.decode(buf); // BOOM!

...

Is throwing:

err: TypeError: Cannot use 'in' operator to search for 'buffer' in null
    at TextDecoder.decode (/Users/runner/work/deno/deno/op_crates/web/08_text_encoding.js:445:18)
    at XMLHttpRequestSham.xhrSend (https://deno.land/x/[email protected]/src/xhrSham.js:293:34)
    at async XMLHttpRequestSham.send (https://deno.land/x/[email protected]/src/xhrSham.js:51:7)

This doesn't occur in Deno 1.3.0, so assume introduced by one of the changes to Deno core in 1.3.1, see the release notes: https://github.com/denoland/deno/releases/tag/v1.3.1

08_text_encoding line ref: https://github.com/denoland/deno/blob/master/op_crates/web/08_text_encoding.js#L445

Reproducing we can see we get the error with:

const decoder = new TextDecoder("utf-8");
const buf = null;
const decoded = decoder.decode(buf as any);

It would seem that response.arrayBuffer() in Deno 1.3.0 returned "" for a null body whereas no it returns null which causes the error in the decoder as it has no null protection.

We can protect against this in SuperDeno by adding our own null check around the decoder.

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.