import fs = require("fs");
import { join } from "path";
import {
  createClient,
  createPartialDone,
  getPort,
  success,
  successFn,
} from "./support/util";
import { Server } from "..";
import expect from "expect.js";

describe("socket", () => {
  it("should not fire events more than once after manually reconnecting", (done) => {
    const io = new Server(0);
    const clientSocket = createClient(io, "/", { reconnection: false });

    clientSocket.on("connect", function init() {
      clientSocket.off("connect", init);
      clientSocket.io.engine.close();

      process.nextTick(() => {
        clientSocket.connect();
      });
      clientSocket.on("connect", successFn(done, io, clientSocket));
    });
  });

  it("should not fire reconnect_failed event more than once when server closed", (done) => {
    const io = new Server(0);
    const clientSocket = createClient(io, "/", {
      reconnectionAttempts: 3,
      reconnectionDelay: 100,
    });

    clientSocket.on("connect", () => {
      io.close();
    });

    clientSocket.io.on("reconnect_failed", () => {
      success(done, io, clientSocket);
    });
  });

  it("should receive events", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("random", (a, b, c) => {
        expect(a).to.be(1);
        expect(b).to.be("2");
        expect(c).to.eql([3]);

        success(done, io, socket);
      });
      socket.emit("random", 1, "2", [3]);
    });
  });

  it("should receive message events through `send`", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("message", (a) => {
        expect(a).to.be(1337);

        success(done, io, socket);
      });
      socket.send(1337);
    });
  });

  it("should error with null messages", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("message", (a) => {
        expect(a).to.be(null);
        success(done, io, socket);
      });
      socket.send(null);
    });
  });

  it("should handle transport null messages", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { reconnection: false });

    io.on("connection", (s) => {
      s.on("error", (err) => {
        expect(err).to.be.an(Error);
        s.on("disconnect", (reason) => {
          expect(reason).to.be("forced close");

          success(done, io, socket);
        });
      });
      (s as any).client.ondata(null);
    });
  });

  it("should emit events", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    socket.on("woot", (a) => {
      expect(a).to.be("tobi");
      success(done, io, socket);
    });
    io.on("connection", (s) => {
      s.emit("woot", "tobi");
    });
  });

  it("should emit events with utf8 multibyte character", (done) => {
    const io = new Server(0);
    const socket = createClient(io);
    let i = 0;

    socket.on("hoot", (a) => {
      expect(a).to.be("utf8 — string");
      i++;

      if (3 == i) {
        success(done, io, socket);
      }
    });
    io.on("connection", (s) => {
      s.emit("hoot", "utf8 — string");
      s.emit("hoot", "utf8 — string");
      s.emit("hoot", "utf8 — string");
    });
  });

  it("should emit events with binary data", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    let imageData;
    socket.on("doge", (a) => {
      expect(Buffer.isBuffer(a)).to.be(true);
      expect(imageData.length).to.equal(a.length);
      expect(imageData[0]).to.equal(a[0]);
      expect(imageData[imageData.length - 1]).to.equal(a[a.length - 1]);

      success(done, io, socket);
    });
    io.on("connection", (s) => {
      fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
        if (err) return done(err);
        imageData = data;
        s.emit("doge", data);
      });
    });
  });

  it("should emit events with several types of data (including binary)", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    socket.on("multiple", (a, b, c, d, e, f) => {
      expect(a).to.be(1);
      expect(Buffer.isBuffer(b)).to.be(true);
      expect(c).to.be("3");
      expect(d).to.eql([4]);
      expect(Buffer.isBuffer(e)).to.be(true);
      expect(Buffer.isBuffer(f[0])).to.be(true);
      expect(f[1]).to.be("swag");
      expect(Buffer.isBuffer(f[2])).to.be(true);

      success(done, io, socket);
    });
    io.on("connection", (s) => {
      fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
        if (err) return done(err);
        const buf = Buffer.from("asdfasdf", "utf8");
        s.emit("multiple", 1, data, "3", [4], buf, [data, "swag", buf]);
      });
    });
  });

  it("should receive events with binary data", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("buff", (a) => {
        expect(Buffer.isBuffer(a)).to.be(true);

        success(done, io, socket);
      });
      const buf = Buffer.from("abcdefg", "utf8");
      socket.emit("buff", buf);
    });
  });

  it("should receive events with several types of data (including binary)", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("multiple", (a, b, c, d, e, f) => {
        expect(a).to.be(1);
        expect(Buffer.isBuffer(b)).to.be(true);
        expect(c).to.be("3");
        expect(d).to.eql([4]);
        expect(Buffer.isBuffer(e)).to.be(true);
        expect(Buffer.isBuffer(f[0])).to.be(true);
        expect(f[1]).to.be("swag");
        expect(Buffer.isBuffer(f[2])).to.be(true);

        success(done, io, socket);
      });
      fs.readFile(join(__dirname, "support", "doge.jpg"), (err, data) => {
        if (err) return done(err);
        const buf = Buffer.from("asdfasdf", "utf8");
        socket.emit("multiple", 1, data, "3", [4], buf, [data, "swag", buf]);
      });
    });
  });

  it("should not emit volatile event after regular event (polling)", (done) => {
    const io = new Server(0, { transports: ["polling"] });

    let counter = 0;
    io.on("connection", (s) => {
      s.emit("ev", "data");
      s.volatile.emit("ev", "data");
    });

    const socket = createClient(io, "/", { transports: ["polling"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should not emit volatile event after regular event (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      s.emit("ev", "data");
      s.volatile.emit("ev", "data");
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should emit volatile event (polling)", (done) => {
    const io = new Server(0, { transports: ["polling"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.volatile.emit("ev", "data");
      }, 100);
    });

    const socket = createClient(io, "/", { transports: ["polling"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 500);
  });

  it("should emit volatile event (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.volatile.emit("ev", "data");
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should emit only one consecutive volatile event (polling)", (done) => {
    const io = new Server(0, { transports: ["polling"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.volatile.emit("ev", "data");
        s.volatile.emit("ev", "data");
      }, 100);
    });

    const socket = createClient(io, "/", { transports: ["polling"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 500);
  });

  it("should emit only one consecutive volatile event (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.volatile.emit("ev", "data");
        s.volatile.emit("ev", "data");
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should emit only one consecutive volatile event with binary (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.volatile.emit("ev", Buffer.from([1, 2, 3]));
        s.volatile.emit("ev", Buffer.from([4, 5, 6]));
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should broadcast only one consecutive volatile event with binary (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        io.volatile.emit("ev", Buffer.from([1, 2, 3]));
        io.volatile.emit("ev", Buffer.from([4, 5, 6]));
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(1);
      success(done, io, socket);
    }, 200);
  });

  it("should emit regular events after trying a failed volatile event (polling)", (done) => {
    const io = new Server(0, { transports: ["polling"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.emit("ev", "data");
        s.volatile.emit("ev", "data");
        s.emit("ev", "data");
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["polling"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(2);
      success(done, io, socket);
    }, 200);
  });

  it("should emit regular events after trying a failed volatile event (ws)", (done) => {
    const io = new Server(0, { transports: ["websocket"] });

    let counter = 0;
    io.on("connection", (s) => {
      // Wait to make sure there are no packets being sent for opening the connection
      setTimeout(() => {
        s.emit("ev", "data");
        s.volatile.emit("ev", "data");
        s.emit("ev", "data");
      }, 20);
    });

    const socket = createClient(io, "/", { transports: ["websocket"] });
    socket.on("ev", () => {
      counter++;
    });

    setTimeout(() => {
      expect(counter).to.be(2);
      success(done, io, socket);
    }, 200);
  });

  it("should emit message events through `send`", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    socket.on("message", (a) => {
      expect(a).to.be("a");
      success(done, io, socket);
    });
    io.on("connection", (s) => {
      s.send("a");
    });
  });

  it("should receive event with callbacks", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("woot", (fn) => {
        fn(1, 2);
      });
      socket.emit("woot", (a, b) => {
        expect(a).to.be(1);
        expect(b).to.be(2);
        success(done, io, socket);
      });
    });
  });

  it("should receive all events emitted from namespaced client immediately and in order", (done) => {
    const io = new Server(0);
    let total = 0;

    io.of("/chat", (s) => {
      s.on("hi", (letter) => {
        total++;
        if (total == 2 && letter == "b") {
          success(done, io, chat);
        } else if (total == 1 && letter != "a") {
          throw new Error("events out of order");
        }
      });
    });

    const chat = createClient(io, "/chat");
    chat.emit("hi", "a");
    setTimeout(() => {
      chat.emit("hi", "b");
    }, 50);
  });

  it("should emit events with callbacks", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      socket.on("hi", (fn) => {
        fn();
      });
      s.emit("hi", () => {
        success(done, io, socket);
      });
    });
  });

  it("should receive events with args and callback", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("woot", (a, b, fn) => {
        expect(a).to.be(1);
        expect(b).to.be(2);
        fn();
      });
      socket.emit("woot", 1, 2, () => {
        success(done, io, socket);
      });
    });
  });

  it("should emit events with args and callback", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      socket.on("hi", (a, b, fn) => {
        expect(a).to.be(1);
        expect(b).to.be(2);
        fn();
      });
      s.emit("hi", 1, 2, () => {
        success(done, io, socket);
      });
    });
  });

  it("should receive events with binary args and callbacks", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("woot", (buf, fn) => {
        expect(Buffer.isBuffer(buf)).to.be(true);
        fn(1, 2);
      });
      socket.emit("woot", Buffer.alloc(3), (a, b) => {
        expect(a).to.be(1);
        expect(b).to.be(2);
        success(done, io, socket);
      });
    });
  });

  it("should emit events with binary args and callback", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      socket.on("hi", (a, fn) => {
        expect(Buffer.isBuffer(a)).to.be(true);
        fn();
      });
      s.emit("hi", Buffer.alloc(4), () => {
        success(done, io, socket);
      });
    });
  });

  it("should emit events and receive binary data in a callback", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      socket.on("hi", (fn) => {
        fn(Buffer.alloc(1));
      });
      s.emit("hi", (a) => {
        expect(Buffer.isBuffer(a)).to.be(true);
        success(done, io, socket);
      });
    });
  });

  it("should receive events and pass binary data in a callback", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      s.on("woot", (fn) => {
        fn(Buffer.alloc(2));
      });
      socket.emit("woot", (a) => {
        expect(Buffer.isBuffer(a)).to.be(true);
        success(done, io, socket);
      });
    });
  });

  it("should emit an event and wait for the acknowledgement", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", async (s) => {
      socket.on("hi", (a, b, fn) => {
        expect(a).to.be(1);
        expect(b).to.be(2);
        fn(3);
      });

      const val = await s.emitWithAck("hi", 1, 2);
      expect(val).to.be(3);

      success(done, io, socket);
    });
  });

  it("should have access to the client", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      expect(s.client).to.be.an("object");
      success(done, io, socket);
    });
  });

  it("should have access to the connection", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      expect(s.client.conn).to.be.an("object");
      expect(s.conn).to.be.an("object");
      success(done, io, socket);
    });
  });

  it("should have access to the request", (done) => {
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", (s) => {
      expect(s.client.request.headers).to.be.an("object");
      expect(s.request.headers).to.be.an("object");
      success(done, io, socket);
    });
  });

  it("should see query parameters in the request", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { query: { key1: 1, key2: 2 } });

    io.on("connection", (s) => {
      const parsed = require("url").parse(s.request.url);
      const query = require("querystring").parse(parsed.query);
      expect(query.key1).to.be("1");
      expect(query.key2).to.be("2");
      success(done, io, socket);
    });
  });

  it("should see query parameters sent from secondary namespace connections in handshake object", (done) => {
    const io = new Server(0);
    const client1 = createClient(io);
    const client2 = createClient(io, "/connection2", {
      auth: { key1: "aa", key2: "&=bb" },
    });
    io.on("connection", (s) => {});
    io.of("/connection2").on("connection", (s) => {
      expect(s.handshake.query.key1).to.be(undefined);
      expect(s.handshake.query.EIO).to.be("4");
      expect(s.handshake.auth.key1).to.be("aa");
      expect(s.handshake.auth.key2).to.be("&=bb");
      success(done, io, client1, client2);
    });
  });

  it("should handshake a client without access to the Engine.IO request (WebTransport-only connection)", (done) => {
    const io = new Server(0);
    const clientSocket = createClient(io, "/");

    io.engine.on("connection", (socket) => {
      delete socket.request;
    });

    io.on("connection", (socket) => {
      expect(socket.handshake.secure).to.be(true);
      expect(socket.handshake.headers).to.eql({});
      expect(socket.handshake.query).to.eql({});

      success(done, io, clientSocket);
    });
  });

  it("should handle very large json", function (done) {
    this.timeout(30000);
    const io = new Server(0, { perMessageDeflate: false });
    let received = 0;

    const socket = createClient(io);
    socket.on("big", (a) => {
      expect(Buffer.isBuffer(a.json)).to.be(false);
      if (++received == 3) success(done, io, socket);
      else socket.emit("big", a);
    });
    io.on("connection", (s) => {
      fs.readFile(join(__dirname, "fixtures", "big.json"), (err, data: any) => {
        if (err) return done(err);
        data = JSON.parse(data);
        s.emit("big", { hello: "friend", json: data });
      });
      s.on("big", (a) => {
        s.emit("big", a);
      });
    });
  });

  it("should handle very large binary data", function (done) {
    this.timeout(30000);
    const io = new Server(0, { perMessageDeflate: false });
    let received = 0;

    const socket = createClient(io);
    socket.on("big", (a) => {
      expect(Buffer.isBuffer(a.image)).to.be(true);
      if (++received == 3) success(done, io, socket);
      else socket.emit("big", a);
    });
    io.on("connection", (s) => {
      fs.readFile(join(__dirname, "fixtures", "big.jpg"), (err, data) => {
        if (err) return done(err);
        s.emit("big", { hello: "friend", image: data });
      });
      s.on("big", (a) => {
        expect(Buffer.isBuffer(a.image)).to.be(true);
        s.emit("big", a);
      });
    });
  });

  it("should be able to emit after server close and restart", (done) => {
    const io = new Server(0);

    io.on("connection", (socket) => {
      socket.on("ev", (data) => {
        expect(data).to.be("payload");
        success(done, io, clientSocket);
      });
    });

    const port = getPort(io);
    const clientSocket = createClient(io, "/", {
      reconnectionAttempts: 10,
      reconnectionDelay: 100,
    });
    clientSocket.once("connect", () => {
      io.close(() => {
        clientSocket.io.on("reconnect", () => {
          clientSocket.emit("ev", "payload");
        });
        io.listen(port);
      });
    });
  });

  it("should enable compression by default", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/chat");

    io.of("/chat").on("connection", (s) => {
      s.conn.once("packetCreate", (packet) => {
        expect(packet.options.compress).to.be(true);
        success(done, io, socket);
      });
      s.emit("woot", "hi");
    });
  });

  it("should disable compression", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/chat");

    io.of("/chat").on("connection", (s) => {
      s.conn.once("packetCreate", (packet) => {
        expect(packet.options.compress).to.be(false);
        success(done, io, socket);
      });
      s.compress(false).emit("woot", "hi");
    });
  });

  it("should error with raw binary and warn", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { reconnection: false });

    io.on("connection", (s) => {
      s.conn.on("upgrade", () => {
        console.log(
          "\u001b[96mNote: warning expected and normal in test.\u001b[39m"
        );
        // @ts-ignore
        socket.io.engine.write("5woooot");
        setTimeout(() => {
          success(done, io, socket);
        }, 100);
      });
    });
  });

  it("should not crash when receiving an error packet without handler", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { reconnection: false });

    io.on("connection", (s) => {
      s.conn.on("upgrade", () => {
        console.log(
          "\u001b[96mNote: warning expected and normal in test.\u001b[39m"
        );
        // @ts-ignore
        socket.io.engine.write('44["handle me please"]');
        setTimeout(() => {
          success(done, io, socket);
        }, 100);
      });
    });
  });

  it("should not crash with raw binary", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { reconnection: false });

    io.on("connection", (s) => {
      s.once("error", (err) => {
        expect(err.message).to.match(/Illegal attachments/);
        success(done, io, socket);
      });
      s.conn.on("upgrade", () => {
        // @ts-ignore
        socket.io.engine.write("5woooot");
      });
    });
  });

  it("should handle empty binary packet", (done) => {
    const io = new Server(0);
    const socket = createClient(io, "/", { reconnection: false });

    io.on("connection", (s) => {
      s.once("error", (err) => {
        expect(err.message).to.match(/Illegal attachments/);
        success(done, io, socket);
      });
      s.conn.on("upgrade", () => {
        // @ts-ignore
        socket.io.engine.write("5");
      });
    });
  });

  it("should not crash when messing with Object prototype (and other globals)", (done) => {
    // @ts-ignore
    Object.prototype.foo = "bar";
    const io = new Server(0);
    const socket = createClient(io);

    io.on("connection", successFn(done, io, socket));
  });

  it("should throw on reserved event", (done) => {
    const io = new Server(0);

    const socket = createClient(io);
    io.on("connection", (s) => {
      expect(() => s.emit("connect_error")).to.throwException(
        /"connect_error" is a reserved event name/
      );
      socket.close();
      success(done, io, socket);
    });
  });

  it("should ignore a packet received after disconnection", (done) => {
    const io = new Server(0);
    const clientSocket = createClient(io);

    io.on("connection", (socket) => {
      socket.on("test", () => {
        done(new Error("should not happen"));
      });
      socket.on("disconnect", successFn(done, io, clientSocket));
    });

    clientSocket.on("connect", () => {
      clientSocket.emit("test", Buffer.alloc(10));
      clientSocket.disconnect();
    });
  });

  it("should leave all rooms joined after a middleware failure", (done) => {
    const io = new Server(0);
    const client = createClient(io, "/");

    io.use((socket, next) => {
      socket.join("room1");
      next(new Error("nope"));
    });

    client.on("connect_error", () => {
      expect(io.of("/").adapter.rooms.size).to.eql(0);

      io.close();
      success(done, io, client);
    });
  });

  it("should not join rooms after disconnection", (done) => {
    const io = new Server(0);
    const client = createClient(io, "/");

    io.on("connection", (socket) => {
      socket.disconnect();
      socket.join("room1");
    });

    client.on("disconnect", () => {
      expect(io.of("/").adapter.rooms.size).to.eql(0);

      io.close();
      success(done, io, client);
    });
  });

  describe("onAny", () => {
    it("should call listener", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      clientSocket.emit("my-event", "123");

      io.on("connection", (socket) => {
        socket.onAny((event, arg1) => {
          expect(event).to.be("my-event");
          expect(arg1).to.be("123");
          success(done, io, clientSocket);
        });
      });
    });

    it("should prepend listener", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      clientSocket.emit("my-event", "123");

      io.on("connection", (socket) => {
        let count = 0;

        socket.onAny((event, arg1) => {
          expect(count).to.be(2);
          success(done, io, clientSocket);
        });

        socket.prependAny(() => {
          expect(count++).to.be(1);
        });

        socket.prependAny(() => {
          expect(count++).to.be(0);
        });
      });
    });

    it("should remove listener", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      clientSocket.emit("my-event", "123");

      io.on("connection", (socket) => {
        const fail = () => done(new Error("fail"));

        socket.onAny(fail);
        socket.offAny(fail);
        expect(socket.listenersAny.length).to.be(0);

        socket.onAny(() => {
          success(done, io, clientSocket);
        });
      });
    });
  });

  describe("onAnyOutgoing", () => {
    it("should call listener", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      io.on("connection", (socket) => {
        socket.onAnyOutgoing((event, arg1) => {
          expect(event).to.be("my-event");
          expect(arg1).to.be("123");

          success(done, io, clientSocket);
        });

        socket.emit("my-event", "123");
      });
    });

    it("should call listener when broadcasting", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      io.on("connection", (socket) => {
        socket.onAnyOutgoing((event, arg1) => {
          expect(event).to.be("my-event");
          expect(arg1).to.be("123");

          success(done, io, clientSocket);
        });

        io.emit("my-event", "123");
      });
    });

    it("should call listener when broadcasting binary data", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      io.on("connection", (socket) => {
        socket.onAnyOutgoing((event, arg1) => {
          expect(event).to.be("my-event");
          expect(arg1).to.be.an(Uint8Array);

          success(done, io, clientSocket);
        });

        io.emit("my-event", Uint8Array.of(1, 2, 3));
      });
    });

    it("should prepend listener", (done) => {
      const io = new Server(0);
      const clientSocket = createClient(io, "/", { multiplex: false });

      io.on("connection", (socket) => {
        let count = 0;

        socket.onAnyOutgoing((event, arg1) => {
          expect(count).to.be(2);

          success(done, io, clientSocket);
        });

        socket.prependAnyOutgoing(() => {
          expect(count++).to.be(1);
        });

        socket.prependAnyOutgoing(() => {
          expect(count++).to.be(0);
        });

        socket.emit("my-event", "123");
      });
    });

    it("should remove listener", (done) => {
      const io = new Server(0);

      const clientSocket = createClient(io, "/", { multiplex: false });

      io.on("connection", (socket) => {
        const fail = () => done(new Error("fail"));

        socket.onAnyOutgoing(fail);
        socket.offAnyOutgoing(fail);
        expect(socket.listenersAnyOutgoing.length).to.be(0);

        socket.onAnyOutgoing(() => {
          success(done, io, clientSocket);
        });

        socket.emit("my-event", "123");
      });
    });

    it("should disconnect all namespaces when calling disconnect(true)", (done) => {
      const io = new Server(0);
      io.of("/foo");
      io.of("/bar");

      const socket1 = createClient(io, "/", {
        transports: ["websocket"],
      });
      const socket2 = createClient(io, "/foo");
      const socket3 = createClient(io, "/bar");

      io.of("/bar").on("connection", (socket) => {
        socket.disconnect(true);
      });

      const partialDone = createPartialDone(
        3,
        successFn(done, io, socket1, socket2, socket3)
      );

      socket1.on("disconnect", partialDone);
      socket2.on("disconnect", partialDone);
      socket3.on("disconnect", partialDone);
    });
  });
});
