/*---
includes: [runTsuite.js, compareObjects.js]
flags: [async]
---*/

import qs from 'querystring';

let escape_tsuite = {
    name: "querystring.escape() tests",
    T: async (params) => {
        let r = qs.escape(params.value);

        if (r !== params.expected) {
            throw Error(`unexpected output "${r}" != "${params.expected}"`);
        }

        return 'SUCCESS';
    },

    tests: [
        { value: '', expected: '' },
        { value: 'baz=fuz', expected: 'baz%3Dfuz' },
        { value: 'abcαdef', expected: 'abc%CE%B1def' },
]};

let parse_tsuite = {
    name: "querystring.parse() tests",
    T: async (params) => {
        let r;
        let unescape = qs.unescape;

        if (params.unescape) {
            qs.unescape = params.unescape;
        }

        try {
            if (params.options !== undefined) {
                r = qs.parse(params.value, params.sep, params.eq, params.options);

            } else if (params.eq !== undefined) {
                r = qs.parse(params.value, params.sep, params.eq);

            } else if (params.sep !== undefined) {
                r = qs.parse(params.value, params.sep);

            } else {
                r = qs.parse(params.value);
            }

        } finally {
            if (params.unescape) {
                qs.unescape = unescape;
            }
        }

        if (!compareObjects(r, params.expected)) {
            throw Error(`unexpected output "${JSON.stringify(r)}" != "${JSON.stringify(params.expected)}"`);
        }

        return 'SUCCESS';
    },

    tests: [
        { value: '', expected: {} },
        { value: 'baz=fuz', expected: { baz:'fuz' } },
        { value: 'baz=fuz', expected: { baz:'fuz' } },
        { value: 'baz=fuz&', expected: { baz:'fuz' } },
        { value: '&baz=fuz', expected: { baz:'fuz' } },
        { value: '&&baz=fuz', expected: { baz:'fuz' } },
        { value: 'baz=fuz&muz=tax', expected: { baz:'fuz', muz:'tax' } },
        { value: 'baz=fuz&baz=bar', expected: { baz:['fuz', 'bar'] } },

        { value: qs.encode({ baz:'fuz', muz:'tax' }), expected: { baz:'fuz', muz:'tax' } },

        { value: 'baz=fuz&baz=bar', sep: '&', eq: '=', expected: { baz:['fuz', 'bar'] } },
        { value: 'baz=fuz&baz=bar&baz=zap', expected: { baz:['fuz', 'bar', 'zap'] } },
        { value: 'baz=', expected: { baz:'' } },
        { value: '=fuz', expected: { '':'fuz' } },
        { value: '=fuz=', expected: { '':'fuz=' } },
        { value: '==fu=z', expected: { '':'=fu=z' } },
        { value: '===fu=z&baz=bar', expected: { baz:'bar', '':'==fu=z' } },
        { value: 'freespace', expected: { freespace:'' } },
        { value: 'name&value=12', expected: { name:'', value:'12' } },
        { value: 'baz=fuz&muz=tax', sep: 'fuz', expected: { baz:'', '&muz':'tax' } },
        { value: 'baz=fuz&muz=tax', sep: '', expected: { baz:'fuz', 'muz':'tax' } },
        { value: 'baz=fuz&muz=tax', sep: null, expected: { baz:'fuz', 'muz':'tax' } },
        { value: 'baz=fuz123muz=tax', sep: 123, expected: { baz:'fuz', 'muz':'tax' } },
        { value: 'baz=fuzαααmuz=tax', sep: 'ααα', expected: { baz:'fuz', 'muz':'tax' } },
        { value: 'baz=fuz&muz=tax', sep: '=', expected: { baz:'', 'fuz&muz':'', 'tax':'' } },

        { value: 'baz=fuz&muz=tax', sep: '', eq: '', expected: { baz:'fuz', muz:'tax' } },
        { value: 'baz=fuz&muz=tax', sep: null, eq: 'fuz', expected: { 'baz=':'','muz=tax':'' } },
        { value: 'baz123fuz&muz123tax', sep: null, eq: '123', expected: { baz:'fuz', 'muz':'tax' } },
        { value: 'bazαααfuz&muzαααtax', sep: null, eq: 'ααα', expected: { baz:'fuz', 'muz':'tax' } },

        { value: 'baz=fuz&muz=tax', sep: null, eq: null, options: { maxKeys: 1 }, expected: { baz:'fuz' } },
        { value: 'baz=fuz&muz=tax', sep: null, eq: null, options: { maxKeys: -1 },
          expected: { baz:'fuz', muz:'tax' } },
        { value: 'baz=fuz&muz=tax', sep: null, eq: null, options: { maxKeys: { valueOf: () => { throw 'Oops'; } }},
          exception: 'Oops' },
        { value: 'baz=fuz&muz=tax', sep: null, eq: null,
          options: { decodeURIComponent: (s) => `|${s}|` }, expected: { '|baz|':'|fuz|', '|muz|':'|tax|' } },
        { value: 'baz=fuz&muz=tax', sep: null, eq: null,
          options: { decodeURIComponent: 123 },
          exception: 'TypeError: option decodeURIComponent is not a function' },
        { value: 'baz=fuz&muz=tax', unescape: (s) => `|${s}|`, expected: { '|baz|':'|fuz|', '|muz|':'|tax|' } },
        { value: 'baz=fuz&muz=tax', unescape: 123,
          exception: 'TypeError: QueryString.unescape is not a function' },

        { value: 'ba%32z=f%32uz', expected: { ba2z:'f2uz' } },
        { value: 'ba%F0%9F%92%A9z=f%F0%9F%92%A9uz', expected: { 'ba💩z':'f💩uz' } },
        { value: '==', expected: { '':'=' } },
        { value: 'baz=%F0%9F%A9', expected: { baz:'�' } },
        { value: 'baz=α%00%01%02α', expected: { baz:'α' + String.fromCharCode(0, 1, 2) + 'α' } },
        { value: 'baz=%F6', expected: { baz:'�' } },
        { value: 'baz=%FG', expected: { baz:'%FG' } },
        { value: 'baz=%F', expected: { baz:'%F' } },
        { value: 'baz=%', expected: { baz:'%' } },
        { value: 'ba+z=f+uz', expected: { 'ba z':'f uz' } },
        { value: 'X=' + 'α'.repeat(33), expected: { X:'α'.repeat(33) } },
        { value: 'X=' + 'α1'.repeat(33), expected: { X:'α1'.repeat(33) } },

        { value: {toString: () => { throw 'Oops'; }}, sep: "&", eq: "=",
          exception: 'TypeError: Cannot convert object to primitive value' },
]};

let stringify_tsuite = {
    name: "querystring.stringify() tests",
    T: async (params) => {
        let r;
        let escape = qs.escape;

        if (params.escape) {
            qs.escape = params.escape;
        }

        try {
            if (params.options !== undefined) {
                r = qs.stringify(params.obj, params.sep, params.eq, params.options);

            } else if (params.eq !== undefined) {
                r = qs.stringify(params.obj, params.sep, params.eq);

            } else if (params.sep !== undefined) {
                r = qs.stringify(params.obj, params.sep);

            } else {
                r = qs.stringify(params.obj);
            }

        } finally {
            if (params.escape) {
                qs.escape = escape;
            }
        }

        if (r !== params.expected) {
            throw Error(`unexpected output "${r}" != "${params.expected}"`);
        }

        return 'SUCCESS';
    },

    tests: [
        { obj: {}, expected: '' },
        { obj: { baz:'fuz', muz:'tax' }, expected: 'baz=fuz&muz=tax' },
        { obj: { baz:['fuz', 'tax'] }, expected: 'baz=fuz&baz=tax' },
        { obj: {'baαz': 'fαuz', 'muαz': 'tαax' }, expected: 'ba%CE%B1z=f%CE%B1uz&mu%CE%B1z=t%CE%B1ax' },
        { obj: {A:'α', 'δ': 'D' }, expected: 'A=%CE%B1&%CE%B4=D' },
        { obj: { baz:'fuz', muz:'tax' }, sep: '*', expected: 'baz=fuz*muz=tax' },
        { obj: { baz:'fuz', muz:'tax' }, sep: null, eq: '^', expected: 'baz^fuz&muz^tax' },
        { obj: { baz:'fuz', muz:'tax' }, sep: '', eq: '', expected: 'baz=fuz&muz=tax' },
        { obj: { baz:'fuz', muz:'tax' }, sep: '?', eq: '/', expected: 'baz/fuz?muz/tax' },
        { obj: { baz:'fuz', muz:'tax' }, sep: null, eq: null, options: { encodeURIComponent: (key) => `|${key}|` },
          expected: '|baz|=|fuz|&|muz|=|tax|' },
        { obj: { baz:'fuz', muz:'tax' }, sep: null, eq: null, options: { encodeURIComponent: 123 },
          exception: 'TypeError: option encodeURIComponent is not a function' },
        { obj: { baz:'fuz', muz:'tax' }, escape: (key) => `|${key}|`, expected: '|baz|=|fuz|&|muz|=|tax|' },
        { obj: { '':'' }, escape: (s) => s.length == 0 ? '#' : s, expected: '#=#' },
        { obj: { baz:'fuz', muz:'tax' }, escape: 123,
          exception: 'TypeError: QueryString.escape is not a function' },

        { obj: qs.decode('baz=fuz&muz=tax'), expected: 'baz=fuz&muz=tax' },

        { obj: '123', expected: '' },
        { obj: 123, expected: '' },
        { obj: { baz:'fuz' }, expected: 'baz=fuz' },
        { obj: { baz:undefined }, expected: 'baz=' },
        { obj: Object.create({ baz:'fuz' }), expected: '' },
        { obj: [], expected: '' },
        { obj: ['a'], expected: '0=a' },
        { obj: ['a', 'b'], expected: '0=a&1=b' },
        { obj: ['', ''], expected: '0=&1=' },
        { obj: [undefined, null, Symbol(), Object(0), Object('test'), Object(false),,,],
          expected: '0=&1=&2=&3=&4=&5=' },
        { obj: [['a', 'b'], ['c', 'd']], expected: '0=a&0=b&1=c&1=d' },
        { obj: [['a',,,], ['b',,,]], expected: '0=a&0=&0=&1=b&1=&1=' },
        { obj: [[,'a','b',,]], expected: '0=&0=a&0=b&0=' },
]};

let unescape_tsuite = {
    name: "querystring.unescape() tests",
    T: async (params) => {
        let r = qs.unescape(params.value);

        if (r !== params.expected) {
            throw Error(`unexpected output "${r}" != "${params.expected}"`);
        }

        return 'SUCCESS';
    },

    tests: [
        { value: '', expected: '' },
        { value: 'baz%3Dfuz', expected: 'baz=fuz' },
        { value: 'abc%CE%B1def', expected: 'abcαdef' },
]};

run([
    escape_tsuite,
    parse_tsuite,
    stringify_tsuite,
    unescape_tsuite,
])
.then($DONE, $DONE);
