#![allow(non_snake_case)]

use project;

#[test]
fn apply() {
    project()
        .file(
            "src/lib.rs",
            r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn apply(this: &js::Function, context: &JsValue, args: &js::Array) -> js::Function {
                this.apply(context, args)
            }
        "#,
        )
        .file(
            "test.ts",
            r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                assert.equal(wasm.apply(Math.max, {}, [1, 2, 3]), 3);

                const arr = [1, 2];
                wasm.apply(Array.prototype.push, arr, [3]);
                assert.equal(arr[2], 3);
            }
        "#,
        )
        .test()
}

#[test]
fn bind() {
    project()
        .file(
            "src/lib.rs",
            r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn bind(this: &js::Function, context: &JsValue) -> js::Function {
                this.bind(context)
            }
        "#,
        )
        .file(
            "test.ts",
            r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                const obj = {
                    a: 0,
                    fn: function () {
                        return this.a + 1;
                    }
                }

                const boundFn = wasm.bind(obj.fn, { a: 41 });
                assert.equal(boundFn(), 42);
            }
        "#,
        )
        .test()
}

#[test]
fn length() {
    project()
        .file(
            "src/lib.rs",
            r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn fn_length(this: &js::Function) -> u32 {
                this.length()
            }
        "#,
        )
        .file(
            "test.ts",
            r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                assert.equal(wasm.fn_length(() => {}), 0);
                assert.equal(wasm.fn_length((a: string) => console.log(a)), 1);
                assert.equal(wasm.fn_length((a: string, b: string) => console.log({ a, b })), 2);

                function fn0() {}
                function fn1(a: string) {
                    console.log(a);
                }
                function fn2(a: string, b: string) {
                    console.log({ a, b });
                }

                assert.equal(wasm.fn_length(fn0), 0);
                assert.equal(wasm.fn_length(fn1), 1);
                assert.equal(wasm.fn_length(fn2), 2);
            }
        "#,
        )
        .test()
}

#[test]
fn name() {
    project()
        .file(
            "src/lib.rs",
            r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn fn_name(this: &js::Function) -> js::JsString {
                this.name()
            }
        "#,
        )
        .file(
            "test.ts",
            r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                function namedFn() {}
                assert.equal(wasm.fn_name(namedFn), 'namedFn');

                assert.equal(wasm.fn_name(namedFn.bind({})), 'bound namedFn');

                const obj = {
                    method: () => {}
                }
                assert.equal(wasm.fn_name(obj.method), 'method');

                assert.equal(wasm.fn_name(new Function()), 'anonymous');

                assert.equal(wasm.fn_name(() => {}), '');

                const closure = () => {};
                assert.equal(wasm.fn_name(closure), 'closure');
            }
        "#,
        )
        .test()
}

#[test]
fn to_string() {
    project()
        .file(
            "src/lib.rs",
            r#"
            #![feature(proc_macro, wasm_custom_section)]

            extern crate wasm_bindgen;
            use wasm_bindgen::prelude::*;
            use wasm_bindgen::js;

            #[wasm_bindgen]
            pub fn get_source_code(this: &js::Function) -> js::JsString {
                this.to_string()
            }
        "#,
        )
        .file(
            "test.ts",
            r#"
            import * as assert from "assert";
            import * as wasm from "./out";

            export function test() {
                function fn1(a: any, b: any) { return a + b }
                const fn2 = (a: number) => console.log(a);

                assert.equal(wasm.get_source_code(fn1), 'function fn1(a, b) { return a + b; }');
                assert.equal(wasm.get_source_code(fn2), 'function (a) { return console.log(a); }');
            }
        "#,
        )
        .test()
}