<commit_before>/*jslint node: true */
'use strict';
var logger = require('../utils/logger');
var serverURL = 'http://localhost:' + (process.env.PORT || 80);
var app = require('../app');
var request = require('superagent');
var testData = require('./data');
var chai = require('chai');
var expect = chai.expect;
chai.use(require('chai-json-schema'));

var server = app.listen(process.env.PORT || 80, function () {
    logger.debug('Express server listening on port ' + server.address().port);
});

/**
 * Tests for the web service
 */
describe('The Mi9 Web Service: ', function () {

    it("Should Return Mi9 Sample Response Given Mi9 Sample Request", function (done) {
        /*
         The sample request provided by Mi9 contains both wanted and unwanted shows
         The sample response provided by Mi9 contains only the wanted shows
         Wanted shows are DRM enabled with at least one episode
         */
        request.post(serverURL)
            .type('application/json')
            .send(testData.sampleRequest)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(7);
                //TODO expect('Content-Type', /json/);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(res.body.response).to.deep.equal(JSON.parse(testData.sampleResponse).response);
                done();
            });
    });

    it("Should Return Only Wanted Shows Given Only Wanted Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyValidShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(7);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(JSON.stringify(res.body)).to.equal(testData.sampleResponse);
                done();
            });
    });

    it.skip("Should Return Wanted Shows 1..4 Given Mi9 Sample Request And Skip Is 0 And Take Is 4", function (done) {
        var req = JSON.stringify({
            payload: JSON.parse(testData.sampleRequest).payload,
            skip: 0,
            take: 4,
            totalRecords: 10
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.body.response).to.have.length(4);
                expect(res.status).to.equal(200);
                done();
            });
    });

    it.skip("Should Return Wanted Shows 5..7 Given Mi9 Sample Request And Skip Is 4 And Take Is 4", function (done) {
        // There are only 7 wanted shows in the sample request, so this second page should include only 3 shows.
        var req = JSON.stringify({
            payload: JSON.parse(testData.sampleRequest).payload,
            skip: 4,
            take: 4,
            totalRecords: 10
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.body.response).to.have.length(3);
                //TODO Test for shows 5,6,7 by name
                expect(res.status).to.equal(200);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Skip + Take > TotalRecords", function (done) {
        var req = JSON.stringify({
            payload: testData.requestWithJustOneShow(),
            skip: 10,
            take: 10,
            totalRecords: 15
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return Single Show Given Single Wanted Show", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithJustOneShow())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(1);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(show).to.have.deep.property('title', '16 Kids and Counting');
                done();
            });
    });

    it("Should Return Empty Response Given Single Unwanted Show", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithJustOneShow())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(1);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(show).to.have.deep.property('title', '16 Kids and Counting');
                done();
            });
    });

    it("Should Return Empty Response Given Only Zero-Episode Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyZeroEpisodeShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body).to.deep.equal(JSON.parse(testData.emptyResponse));
                done();
            });
    });

    it("Should Return Empty Response Given Only Non-DRM Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyNonDRMShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body).to.deep.equal(JSON.parse(testData.emptyResponse));
                done();
            });
    });

    it("Should Return 404 Not Found Given Unknown Path", function (done) {
        request.post(serverURL + "/invalidPath").end(function (err, res) {
            expect(res.ok).to.be.false;
            expect(res.status).to.equal(404);
            done();
        });
    });

    it("Should Return 400 Parse Error Given Missing Request", function (done) {
        request.post(serverURL).end(function (err, res) {
            expect(res.ok).to.be.false;
            expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
            expect(res.status).to.equal(400);
            done();
        });
    });

    it("Should Return 400 Parse Error Given Null Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(null)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty JSON Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send({})
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty String Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send('')
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty Payload", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithEmptyPayload)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Invalid Schema", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send({payloads: [], name: "invalid"})
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Invalid MIME Type", function (done) {
        request.post(serverURL)
            .type('text/plain')
            .send(testData.sampleRequest)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Non-JSON Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send("invalid text")
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

});

/**
 * Last-ditch Error Handler (main is in app.js)
 */
process.on('uncaughtException', function (err) {
    logger.error((new Date()).toUTCString() + ' Uncaught Exception:', err.message);
    logger.error(err.stack);
    // let it die because it will be restarted
});<commit_msg>Add server.close to test harness<commit_after>/*jslint node: true */
'use strict';
var logger = require('../utils/logger');
var serverURL = 'http://localhost:' + (process.env.PORT || 80);
var app = require('../app');
var request = require('superagent');
var testData = require('./data');
var chai = require('chai');
var expect = chai.expect;
chai.use(require('chai-json-schema'));

var server = app.listen(process.env.PORT || 80, function () {
    logger.debug('Express server listening on port ' + server.address().port);
});

/**
 * Tests for the web service
 */
describe('The Mi9 Web Service: ', function () {

    it("Should Return Mi9 Sample Response Given Mi9 Sample Request", function (done) {
        /*
         The sample request provided by Mi9 contains both wanted and unwanted shows
         The sample response provided by Mi9 contains only the wanted shows
         Wanted shows are DRM enabled with at least one episode
         */
        request.post(serverURL)
            .type('application/json')
            .send(testData.sampleRequest)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(7);
                //TODO expect('Content-Type', /json/);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(res.body.response).to.deep.equal(JSON.parse(testData.sampleResponse).response);
                done();
            });
    });

    it("Should Return Only Wanted Shows Given Only Wanted Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyValidShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(7);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(JSON.stringify(res.body)).to.equal(testData.sampleResponse);
                done();
            });
    });

    it.skip("Should Return Wanted Shows 1..4 Given Mi9 Sample Request And Skip Is 0 And Take Is 4", function (done) {
        var req = JSON.stringify({
            payload: JSON.parse(testData.sampleRequest).payload,
            skip: 0,
            take: 4,
            totalRecords: 10
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.body.response).to.have.length(4);
                expect(res.status).to.equal(200);
                done();
            });
    });

    it.skip("Should Return Wanted Shows 5..7 Given Mi9 Sample Request And Skip Is 4 And Take Is 4", function (done) {
        // There are only 7 wanted shows in the sample request, so this second page should include only 3 shows.
        var req = JSON.stringify({
            payload: JSON.parse(testData.sampleRequest).payload,
            skip: 4,
            take: 4,
            totalRecords: 10
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.body.response).to.have.length(3);
                //TODO Test for shows 5,6,7 by name
                expect(res.status).to.equal(200);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Skip + Take > TotalRecords", function (done) {
        var req = JSON.stringify({
            payload: testData.requestWithJustOneShow(),
            skip: 10,
            take: 10,
            totalRecords: 15
        });
        request.post(serverURL)
            .type('application/json')
            .send(req)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return Single Show Given Single Wanted Show", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithJustOneShow())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(1);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(show).to.have.deep.property('title', '16 Kids and Counting');
                done();
            });
    });

    it("Should Return Empty Response Given Single Unwanted Show", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithJustOneShow())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body.response).to.have.length(1);
                var show = res.body.response[0];
                expect(show).to.have.property('image');
                expect(show).to.have.property('slug');
                expect(show).to.have.property('title');
                expect(show).to.have.deep.property('title', '16 Kids and Counting');
                done();
            });
    });

    it("Should Return Empty Response Given Only Zero-Episode Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyZeroEpisodeShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body).to.deep.equal(JSON.parse(testData.emptyResponse));
                done();
            });
    });

    it("Should Return Empty Response Given Only Non-DRM Shows", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithOnlyNonDRMShows())
            .end(function (err, res) {
                expect(res.ok).to.be.true;
                expect(res.status).to.equal(200);
                expect(res.body).to.deep.equal(JSON.parse(testData.emptyResponse));
                done();
            });
    });

    it("Should Return 404 Not Found Given Unknown Path", function (done) {
        request.post(serverURL + "/invalidPath").end(function (err, res) {
            expect(res.ok).to.be.false;
            expect(res.status).to.equal(404);
            done();
        });
    });

    it("Should Return 400 Parse Error Given Missing Request", function (done) {
        request.post(serverURL).end(function (err, res) {
            expect(res.ok).to.be.false;
            expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
            expect(res.status).to.equal(400);
            done();
        });
    });

    it("Should Return 400 Parse Error Given Null Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(null)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty JSON Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send({})
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty String Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send('')
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Empty Payload", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send(testData.requestWithEmptyPayload)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Invalid Schema", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send({payloads: [], name: "invalid"})
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Invalid MIME Type", function (done) {
        request.post(serverURL)
            .type('text/plain')
            .send(testData.sampleRequest)
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    it("Should Return 400 Parse Error Given Non-JSON Request", function (done) {
        request.post(serverURL)
            .type('application/json')
            .send("invalid text")
            .end(function (err, res) {
                expect(res.ok).to.be.false;
                expect(JSON.stringify(res.body)).to.equal(testData.expectedJSONParseErrorMsg);
                expect(res.status).to.equal(400);
                done();
            });
    });

    after(function () {
        server.close();
        logger.debug('Express server is now closed.');
    });
});

/**
 * Last-ditch Error Handler (main is in app.js)
 */
process.on('uncaughtException', function (err) {
    logger.error((new Date()).toUTCString() + ' Uncaught Exception:', err.message);
    logger.error(err.stack);
    // let it die because it will be restarted
});<|endoftext|>


<commit_before>'use strict'
/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if(typeof global.it === 'function') {
  return;
}

var bone = require("../lib/Bone.js");
let fConfig = require('../net2/config.js').getConfig();
let License = require('../util/license');
let SysManager = require('../net2/SysManager.js');
let sysManager = new SysManager('info');
let sampledata = require("../test/sample_data_intel.js");

/*
setTimeout(() => {
    bone.log("error", {
        msg: "test"
    });
}, 3000);
*/
/*
setTimeout(() => {
//    bone.device("identify", {});
    //bone.intel("*", "check", {iplist:[ 'www.slackbitch.com', '103.224.182.253' ]});
    bone.hashset("ad_cn",(err,data)=>{
//	console.log(typeof(data));
      let d = JSON.parse(data)
      for(let e in d) {
        console.log(d[e]);
      }
    });
}, 5000);
*/
let flowUtil = require("../net2/FlowUtil.js");

setTimeout(()=>{

     sysManager.getSysInfo((err,_sysinfo) => {
        let license = License.getLicense();
        console.log(license);
        if(err) {
          reject(err);
          return;
        }

        console.log("Checking in Cloud...");

        bone.checkin(fConfig, license, _sysinfo, (err,data) => {
            console.log(data,err);
            let mac = "9C:AD:EF:01:02:03";
            let rawData = {
                ou: mac.slice(0,13), // use 0,13 for better OU compatibility
                uuid: flowUtil.hashMac(mac)
            };
            bone.device("identify", rawData,(err,data)=>{
                console.log(data);
              //  bone.getLicense("81244056-90b9-43f3-a5ae-8681bde09e58","thisismac",(err,data)=>{
              //      console.log(err,data);
              //  });

                 let sdata = {flowlist:sampledata.netflix, hashed:1};

                 bone.intel("*","check", sdata, (err, data) => {
                     console.log(data);
                 });

            });

        });
      });
},2000);

<commit_msg>another ugly ... but simple sanity test<commit_after>'use strict'
/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if(typeof global.it === 'function') {
  return;
}

var bone = require("../lib/Bone.js");
let fConfig = require('../net2/config.js').getConfig();
let License = require('../util/license');
let SysManager = require('../net2/SysManager.js');
let sysManager = new SysManager('info');
let sampledata = require("../tests/sample_data_intel.js");

/*
setTimeout(() => {
    bone.log("error", {
        msg: "test"
    });
}, 3000);
*/
/*
setTimeout(() => {
//    bone.device("identify", {});
    //bone.intel("*", "check", {iplist:[ 'www.slackbitch.com', '103.224.182.253' ]});
    bone.hashset("ad_cn",(err,data)=>{
//	console.log(typeof(data));
      let d = JSON.parse(data)
      for(let e in d) {
        console.log(d[e]);
      }
    });
}, 5000);
*/
let flowUtil = require("../net2/FlowUtil.js");

setTimeout(()=>{

     sysManager.getSysInfo((err,_sysinfo) => {
        let license = License.getLicense();
        console.log(license);
        if(err) {
          reject(err);
          return;
        }

        console.log("Checking in Cloud...");

        bone.checkin(fConfig, license, _sysinfo, (err,data) => {
            console.log(data,err);
            let mac = "9C:AD:EF:01:02:03";
            let rawData = {
                ou: mac.slice(0,13), // use 0,13 for better OU compatibility
                uuid: flowUtil.hashMac(mac)
            };
    bone.log("error", {
        msg: "Bone Log Test"
    }, (err,data)=> {
            bone.device("identify", rawData,(err,data)=>{
                console.log(data);
              //  bone.getLicense("81244056-90b9-43f3-a5ae-8681bde09e58","thisismac",(err,data)=>{
              //      console.log(err,data);
              //  });

                 let sdata = {flowlist:sampledata.netflix, hashed:1};

                 bone.intel("*","check", sdata, (err, data) => {
                     console.log(data);
                 });

            });
    });

        });
      });
},2000);

<|endoftext|>


<commit_before>/**
 * External dependencies
 */
import castArray from 'lodash/castArray';
import mapValues from 'lodash/mapValues';
import fetchMock from 'fetch-mock';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import coreSiteStore from '../../assets/js/googlesitekit/datastore/site';
import { STORE_NAME as CORE_SITE } from '../../assets/js/googlesitekit/datastore/site/constants';
import coreUserStore from '../../assets/js/googlesitekit/datastore/user';
import { STORE_NAME as CORE_USER } from '../../assets/js/googlesitekit/datastore/user/constants';
import coreFormsStore from '../../assets/js/googlesitekit/datastore/forms';
import { STORE_NAME as CORE_FORMS } from '../../assets/js/googlesitekit/datastore/forms/constants';
import coreModulesStore from '../../assets/js/googlesitekit/modules/datastore';
import { STORE_NAME as CORE_MODULES } from '../../assets/js/googlesitekit/modules/datastore/constants';
import coreWidgetsStore from '../../assets/js/googlesitekit/widgets/datastore';
import { STORE_NAME as CORE_WIDGETS } from '../../assets/js/googlesitekit/widgets/datastore/constants';
import modulesAdSenseStore from '../../assets/js/modules/adsense/datastore';
import { STORE_NAME as MODULES_ADSENSE } from '../../assets/js/modules/adsense/datastore/constants';
import modulesAnalyticsStore from '../../assets/js/modules/analytics/datastore';
import { STORE_NAME as MODULES_ANALYTICS } from '../../assets/js/modules/analytics/datastore/constants';
import modulesPageSpeedInsightsStore from '../../assets/js/modules/pagespeed-insights/datastore';
import { STORE_NAME as MODULES_PAGESPEED_INSIGHTS } from '../../assets/js/modules/pagespeed-insights/datastore/constants';
import modulesSearchConsoleStore from '../../assets/js/modules/search-console/datastore';
import { STORE_NAME as MODULES_SEARCH_CONSOLE } from '../../assets/js/modules/search-console/datastore/constants';
import modulesTagManagerStore from '../../assets/js/modules/tagmanager/datastore';
import { STORE_NAME as MODULES_TAGMANAGER } from '../../assets/js/modules/tagmanager/datastore/constants';
import modulesOptimizeStore from '../../assets/js/modules/optimize/datastore';
import { STORE_NAME as MODULES_OPTIMIZE } from '../../assets/js/modules/optimize/datastore/constants';
import coreModulesFixture from '../../assets/js/googlesitekit/modules/datastore/fixtures.json';
import AdsenseIcon from '../../assets/svg/adsense.svg';
import AnalyticsIcon from '../../assets/svg/analytics.svg';
import PagespeedInsightsIcon from '../../assets/svg/pagespeed-insights.svg';
import SearchConsoleIcon from '../../assets/svg/search-console.svg';
import TagManagerIcon from '../../assets/svg/tagmanager.svg';
import OptimizeIcon from '../../assets/svg/optimize.svg';

/**
 * Creates a registry with all available stores.
 *
 * @since 1.5.0
 * @private
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 * @private
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	// Populate most basic data which should not affect any tests.
	provideUserInfo( registry );

	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Provides site connection data to the given registry.
 *
 * By default the site will be set to connected.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteConnection = ( registry, extraData = {} ) => {
	const defaultConnected = extraData.connected !== undefined ? extraData.connected : true;
	const defaults = {
		connected: defaultConnected,
		resettable: defaultConnected,
		setupCompleted: defaultConnected,
		hasConnectedAdmins: defaultConnected,
		ownerID: defaultConnected ? 1 : 0,
	};

	registry.dispatch( CORE_SITE ).receiveGetConnection( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user authentication data to the given registry.
 *
 * By default the user will be set to authenticated.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserAuthentication = ( registry, extraData = {} ) => {
	const defaults = {
		authenticated: true,
		requiredScopes: [],
		grantedScopes: [],
		unsatisfiedScopes: [],
		needsReauthentication: [],
	};

	const mergedData = { ...defaults, ...extraData };
	registry.dispatch( CORE_USER ).receiveGetAuthentication( mergedData );

	// Also set verification info here based on authentication.
	registry.dispatch( CORE_USER ).receiveUserIsVerified( mergedData.authenticated );
};

/**
 * Provides site information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteInfo = ( registry, extraData = {} ) => {
	const defaults = {
		adminURL: 'http://example.com/wp-admin',
		ampMode: false,
		currentEntityID: null,
		currentEntityTitle: null,
		currentEntityType: null,
		currentEntityURL: null,
		homeURL: 'http://example.com',
		proxyPermissionsURL: 'https://sitekit.withgoogle.com/site-management/permissions/',
		proxySetupURL: 'https://sitekit.withgoogle.com/site-management/setup/',
		referenceSiteURL: 'http://example.com',
		siteName: 'My Site Name',
		timezone: 'America/Detroit',
		usingProxy: true,
	};

	registry.dispatch( CORE_SITE ).receiveSiteInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserInfo = ( registry, extraData = {} ) => {
	const defaults = {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	};

	registry.dispatch( CORE_USER ).receiveUserInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Registers the given module with settings to the given registry.
 *
 * @since n.e.x.t
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module objects to be merged with defaults. Default empty array.
 */
export const provideModuleRegistrations = ( registry, extraData = [] ) => {
	const moduleIconMap = {
		adsense: AdsenseIcon,
		analytics: AnalyticsIcon,
		optimize: OptimizeIcon,
		'pagespeed-insights': PagespeedInsightsIcon,
		'search-console': SearchConsoleIcon,
		tagmanager: TagManagerIcon,
	};

	for ( const slug in moduleIconMap ) {
		registry.dispatch( CORE_MODULES ).registerModule( slug, { ...extraData[ slug ], icon: moduleIconMap[ slug ] } );
	}
};

/**
 * Provides modules data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module objects to be merged with defaults. Default empty array.
 */
export const provideModules = ( registry, extraData = [] ) => {
	const extraModules = extraData.reduce( ( acc, module ) => {
		return { ...acc, [ module.slug ]: module };
	}, {} );

	const moduleSlugs = coreModulesFixture.map( ( { slug } ) => slug );
	const modules = coreModulesFixture
		.map( ( module ) => {
			if ( extraModules[ module.slug ] ) {
				return { ...module, ...extraModules[ module.slug ] };
			}
			return { ...module };
		} )
		.concat(
			extraData.filter( ( { slug } ) => ! moduleSlugs.includes( slug ) ),
		)
    ;

	registry.dispatch( CORE_MODULES ).receiveGetModules( modules );
};

/**
 * Mutes a fetch request to the given URL once.
 *
 * Useful for mocking a request for the purpose of preventing a fetch error
 * where the response itself is not significant but the request should not fail.
 * Sometimes a different response may be required to match the expected type,
 * but for anything else, a full mock should be used.
 *
 * @since 1.10.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher    Criteria for deciding which requests to mock.
 *                                                        (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 * @param {*}                                   [response] Optional. Response to return.
 */
export const muteFetch = ( matcher, response = {} ) => {
	fetchMock.once( matcher, { body: response, status: 200 } );
};

/**
 * Mocks a fetch request in a way so that a response is never returned.
 *
 * Useful for simulating a loading state.
 *
 * @since 1.12.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher Criteria for deciding which requests to mock.
 *                                                      (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 */
export const freezeFetch = ( matcher ) => {
	fetchMock.once( matcher, new Promise( () => {
	} ) );
};

/**
 * Registers all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 * @private
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	registry.registerStore( CORE_SITE, coreSiteStore );
	registry.registerStore( CORE_USER, coreUserStore );
	registry.registerStore( CORE_FORMS, coreFormsStore );
	registry.registerStore( CORE_MODULES, coreModulesStore );
	registry.registerStore( CORE_WIDGETS, coreWidgetsStore );
	registry.registerStore( MODULES_ADSENSE, modulesAdSenseStore );
	registry.registerStore( MODULES_ANALYTICS, modulesAnalyticsStore );
	registry.registerStore( MODULES_PAGESPEED_INSIGHTS, modulesPageSpeedInsightsStore );
	registry.registerStore( MODULES_SEARCH_CONSOLE, modulesSearchConsoleStore );
	registry.registerStore( MODULES_TAGMANAGER, modulesTagManagerStore );
	registry.registerStore( MODULES_OPTIMIZE, modulesOptimizeStore );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

/**
 * Returns an object that returns hasFinishedResolution selectors for each key
 * that are bound to the given registry and store name.
 *
 * @example
 * await untilResolved( registry, STORE_NAME ).selectorWithResolver( arg1, arg2, arg3 );
 *
 * @since 1.11.0
 * @private
 *
 * @param {Object} registry  WP data registry instance.
 * @param {string} storeName Store name the selector belongs to.
 * @return {Object} Object with keys as functions for each resolver in the given store.
 */
export const untilResolved = ( registry, storeName ) => {
	return mapValues(
		registry.stores[ storeName ].resolvers || {},
		( resolverFn, resolverName ) => ( ...args ) => {
			return subscribeUntil(
				registry,
				() => registry.select( storeName ).hasFinishedResolution( resolverName, args ),
			);
		},
	);
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Returns a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 * @private
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.',
	) );
};
<commit_msg>Complete provideModuleRegistrations test utility function.<commit_after>/**
 * External dependencies
 */
import castArray from 'lodash/castArray';
import mapValues from 'lodash/mapValues';
import fetchMock from 'fetch-mock';

/**
 * WordPress dependencies
 */
import { createRegistry, RegistryProvider } from '@wordpress/data';

/**
 * Internal dependencies
 */
import coreSiteStore from '../../assets/js/googlesitekit/datastore/site';
import { STORE_NAME as CORE_SITE } from '../../assets/js/googlesitekit/datastore/site/constants';
import coreUserStore from '../../assets/js/googlesitekit/datastore/user';
import { STORE_NAME as CORE_USER } from '../../assets/js/googlesitekit/datastore/user/constants';
import coreFormsStore from '../../assets/js/googlesitekit/datastore/forms';
import { STORE_NAME as CORE_FORMS } from '../../assets/js/googlesitekit/datastore/forms/constants';
import coreModulesStore from '../../assets/js/googlesitekit/modules/datastore';
import { STORE_NAME as CORE_MODULES } from '../../assets/js/googlesitekit/modules/datastore/constants';
import coreWidgetsStore from '../../assets/js/googlesitekit/widgets/datastore';
import { STORE_NAME as CORE_WIDGETS } from '../../assets/js/googlesitekit/widgets/datastore/constants';
// AdSense.
import modulesAdSenseStore from '../../assets/js/modules/adsense/datastore';
import { STORE_NAME as MODULES_ADSENSE } from '../../assets/js/modules/adsense/datastore/constants';
import { SetupMain as AdSenseSetupMain } from '../../assets/js/modules/adsense/components/setup';
import {
	SettingsEdit as AdSenseSettingsEdit,
	SettingsView as AdSenseSettingsView,
} from '../../assets/js/modules/adsense/components/settings';
import AdSenseIcon from '../../assets/svg/adsense.svg';
import { ERROR_CODE_ADBLOCKER_ACTIVE } from '../../assets/js/modules/adsense/constants';
// Analytics.
import modulesAnalyticsStore from '../../assets/js/modules/analytics/datastore';
import { STORE_NAME as MODULES_ANALYTICS } from '../../assets/js/modules/analytics/datastore/constants';
import { SetupMain as AnalyticsSetupMain } from '../../assets/js/modules/analytics/components/setup';
import {
	SettingsEdit as AnalyticsSettingsEdit,
	SettingsView as AnalyticsSettingsView,
} from '../../assets/js/modules/analytics/components/settings';
import AnalyticsIcon from '../../assets/svg/analytics.svg';
// PageSpeed Insights.
import modulesPageSpeedInsightsStore from '../../assets/js/modules/pagespeed-insights/datastore';
import { STORE_NAME as MODULES_PAGESPEED_INSIGHTS } from '../../assets/js/modules/pagespeed-insights/datastore/constants';
import { SettingsView as PageSpeedInsightsSettingsView } from '../../assets/js/modules/pagespeed-insights/components/settings';
import PageSpeedInsightsIcon from '../../assets/svg/pagespeed-insights.svg';
// Search Console.
import modulesSearchConsoleStore from '../../assets/js/modules/search-console/datastore';
import { STORE_NAME as MODULES_SEARCH_CONSOLE } from '../../assets/js/modules/search-console/datastore/constants';
import { SettingsView as SearchConsoleSettingsView } from '../../assets/js/modules/search-console/components/settings';
import SearchConsoleIcon from '../../assets/svg/search-console.svg';
// Tag Manager.
import modulesTagManagerStore from '../../assets/js/modules/tagmanager/datastore';
import { STORE_NAME as MODULES_TAGMANAGER } from '../../assets/js/modules/tagmanager/datastore/constants';
import { SetupMain as TagManagerSetupMain } from '../../assets/js/modules/tagmanager/components/setup';
import {
	SettingsEdit as TagManagerSettingsEdit,
	SettingsView as TagManagerSettingsView,
} from '../../assets/js/modules/tagmanager/components/settings';
import TagManagerIcon from '../../assets/svg/tagmanager.svg';
// Optimize.
import modulesOptimizeStore from '../../assets/js/modules/optimize/datastore';
import { STORE_NAME as MODULES_OPTIMIZE } from '../../assets/js/modules/optimize/datastore/constants';
import { SetupMain as OptimizeSetupMain } from '../../assets/js/modules/optimize/components/setup';
import {
	SettingsEdit as OptimizeSettingsEdit,
	SettingsView as OptimizeSettingsView,
} from '../../assets/js/modules/optimize/components/settings';
import OptimizeIcon from '../../assets/svg/optimize.svg';
import coreModulesFixture from '../../assets/js/googlesitekit/modules/datastore/fixtures.json';

/**
 * Creates a registry with all available stores.
 *
 * @since 1.5.0
 * @private
 *
 * @return {wp.data.registry} Registry with all available stores registered.
 */
export const createTestRegistry = () => {
	const registry = createRegistry();

	// Register all available stores on the registry.
	registerAllStoresOn( registry );

	return registry;
};

/**
 * Wraps children components with a fresh test registry,
 * which can be configured by its callback prop.
 *
 * @since 1.7.1
 * @private
 *
 * @param {?Object}   props          Component props.
 * @param {?Function} props.callback Function which receives the registry instance.
 * @param {?Object}   props.registry Registry object; uses `createTestRegistry()` by default.
 * @return {WPElement} Wrapped components.
 */
export function WithTestRegistry( { children, callback, registry = createTestRegistry() } = {} ) {
	// Populate most basic data which should not affect any tests.
	provideUserInfo( registry );

	if ( callback ) {
		callback( registry );
	}

	return (
		<RegistryProvider value={ registry }>
			{ children }
		</RegistryProvider>
	);
}

/**
 * Provides site connection data to the given registry.
 *
 * By default the site will be set to connected.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteConnection = ( registry, extraData = {} ) => {
	const defaultConnected = extraData.connected !== undefined ? extraData.connected : true;
	const defaults = {
		connected: defaultConnected,
		resettable: defaultConnected,
		setupCompleted: defaultConnected,
		hasConnectedAdmins: defaultConnected,
		ownerID: defaultConnected ? 1 : 0,
	};

	registry.dispatch( CORE_SITE ).receiveGetConnection( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user authentication data to the given registry.
 *
 * By default the user will be set to authenticated.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserAuthentication = ( registry, extraData = {} ) => {
	const defaults = {
		authenticated: true,
		requiredScopes: [],
		grantedScopes: [],
		unsatisfiedScopes: [],
		needsReauthentication: [],
	};

	const mergedData = { ...defaults, ...extraData };
	registry.dispatch( CORE_USER ).receiveGetAuthentication( mergedData );

	// Also set verification info here based on authentication.
	registry.dispatch( CORE_USER ).receiveUserIsVerified( mergedData.authenticated );
};

/**
 * Provides site information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideSiteInfo = ( registry, extraData = {} ) => {
	const defaults = {
		adminURL: 'http://example.com/wp-admin',
		ampMode: false,
		currentEntityID: null,
		currentEntityTitle: null,
		currentEntityType: null,
		currentEntityURL: null,
		homeURL: 'http://example.com',
		proxyPermissionsURL: 'https://sitekit.withgoogle.com/site-management/permissions/',
		proxySetupURL: 'https://sitekit.withgoogle.com/site-management/setup/',
		referenceSiteURL: 'http://example.com',
		siteName: 'My Site Name',
		timezone: 'America/Detroit',
		usingProxy: true,
	};

	registry.dispatch( CORE_SITE ).receiveSiteInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides user information data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object} registry    Registry object to dispatch to.
 * @param {Object} [extraData] Custom data to set, will be merged with defaults. Default empty object.
 */
export const provideUserInfo = ( registry, extraData = {} ) => {
	const defaults = {
		id: 1,
		name: 'Wapuu WordPress',
		email: 'wapuu.wordpress@gmail.com',
		picture: 'https://wapu.us/wp-content/uploads/2017/11/WapuuFinal-100x138.png',
	};

	registry.dispatch( CORE_USER ).receiveUserInfo( {
		...defaults,
		...extraData,
	} );
};

/**
 * Provides modules data to the given registry.
 *
 * @since 1.17.0
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module objects to be merged with defaults. Default empty array.
 */
export const provideModules = ( registry, extraData = [] ) => {
	const extraModules = extraData.reduce( ( acc, module ) => {
		return { ...acc, [ module.slug ]: module };
	}, {} );

	const moduleSlugs = coreModulesFixture.map( ( { slug } ) => slug );
	const modules = coreModulesFixture
		.map( ( module ) => {
			if ( extraModules[ module.slug ] ) {
				return { ...module, ...extraModules[ module.slug ] };
			}
			return { ...module };
		} )
		.concat(
			extraData.filter( ( { slug } ) => ! moduleSlugs.includes( slug ) ),
		)
    ;

	registry.dispatch( CORE_MODULES ).receiveGetModules( modules );
};

/**
 * Provides module registration data to the given registry.
 *
 * @since n.e.x.t
 * @private
 *
 * @param {Object}   registry    Registry object to dispatch to.
 * @param {Object[]} [extraData] List of module registration data objects to be merged with defaults. Default empty array.
 */
export const provideModuleRegistrations = ( registry, extraData = [] ) => {
	const moduleRegistrationData = {
		adsense: {
			settingsEditComponent: AdSenseSettingsEdit,
			settingsViewComponent: AdSenseSettingsView,
			setupComponent: AdSenseSetupMain,
			icon: AdSenseIcon,
			checkRequirements: () => {
				// TODO: Remove this duplicate-ish code and instead import from reusable AdSense utility function.
				const isAdBlockerActive = registry.select( MODULES_ADSENSE ).isAdBlockerActive();
				if ( ! isAdBlockerActive ) {
					return;
				}

				throw {
					code: ERROR_CODE_ADBLOCKER_ACTIVE,
					message: 'Ad blocker detected, you need to disable it in order to set up AdSense.',
					data: null,
				};
			},
		},
		analytics: {
			settingsEditComponent: AnalyticsSettingsEdit,
			settingsViewComponent: AnalyticsSettingsView,
			setupComponent: AnalyticsSetupMain,
			icon: AnalyticsIcon,
		},
		optimize: {
			settingsEditComponent: OptimizeSettingsEdit,
			settingsViewComponent: OptimizeSettingsView,
			setupComponent: OptimizeSetupMain,
			icon: OptimizeIcon,
		},
		'pagespeed-insights': {
			settingsViewComponent: PageSpeedInsightsSettingsView,
			icon: PageSpeedInsightsIcon,
		},
		'search-console': {
			settingsViewComponent: SearchConsoleSettingsView,
			icon: SearchConsoleIcon,
		},
		tagmanager: {
			settingsEditComponent: TagManagerSettingsEdit,
			settingsViewComponent: TagManagerSettingsView,
			setupComponent: TagManagerSetupMain,
			icon: TagManagerIcon,
		},
	};

	for ( const slug in moduleRegistrationData ) {
		registry.dispatch( CORE_MODULES ).registerModule( slug, { ...moduleRegistrationData[ slug ], ...extraData[ slug ] } );
	}
};

/**
 * Mutes a fetch request to the given URL once.
 *
 * Useful for mocking a request for the purpose of preventing a fetch error
 * where the response itself is not significant but the request should not fail.
 * Sometimes a different response may be required to match the expected type,
 * but for anything else, a full mock should be used.
 *
 * @since 1.10.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher    Criteria for deciding which requests to mock.
 *                                                        (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 * @param {*}                                   [response] Optional. Response to return.
 */
export const muteFetch = ( matcher, response = {} ) => {
	fetchMock.once( matcher, { body: response, status: 200 } );
};

/**
 * Mocks a fetch request in a way so that a response is never returned.
 *
 * Useful for simulating a loading state.
 *
 * @since 1.12.0
 * @private
 *
 * @param {(string|RegExp|Function|URL|Object)} matcher Criteria for deciding which requests to mock.
 *                                                      (@link https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher)
 */
export const freezeFetch = ( matcher ) => {
	fetchMock.once( matcher, new Promise( () => {
	} ) );
};

/**
 * Registers all Site Kit stores on a registry.
 *
 * Use this to register every available Site Kit store on a registry.
 * Useful for testing, when you want to ensure that every registry is
 * available for connected components and data store tests to use.
 *
 * @since 1.5.0
 * @private
 *
 * @param {wp.data.registry} registry Registry to register each store on.
 */
export const registerAllStoresOn = ( registry ) => {
	registry.registerStore( CORE_SITE, coreSiteStore );
	registry.registerStore( CORE_USER, coreUserStore );
	registry.registerStore( CORE_FORMS, coreFormsStore );
	registry.registerStore( CORE_MODULES, coreModulesStore );
	registry.registerStore( CORE_WIDGETS, coreWidgetsStore );
	registry.registerStore( MODULES_ADSENSE, modulesAdSenseStore );
	registry.registerStore( MODULES_ANALYTICS, modulesAnalyticsStore );
	registry.registerStore( MODULES_PAGESPEED_INSIGHTS, modulesPageSpeedInsightsStore );
	registry.registerStore( MODULES_SEARCH_CONSOLE, modulesSearchConsoleStore );
	registry.registerStore( MODULES_TAGMANAGER, modulesTagManagerStore );
	registry.registerStore( MODULES_OPTIMIZE, modulesOptimizeStore );
};

const unsubscribes = [];
export const subscribeWithUnsubscribe = ( registry, ...args ) => {
	const unsubscribe = registry.subscribe( ...args );
	unsubscribes.push( unsubscribe );
	return unsubscribe;
};

/**
 * Returns an object that returns hasFinishedResolution selectors for each key
 * that are bound to the given registry and store name.
 *
 * @example
 * await untilResolved( registry, STORE_NAME ).selectorWithResolver( arg1, arg2, arg3 );
 *
 * @since 1.11.0
 * @private
 *
 * @param {Object} registry  WP data registry instance.
 * @param {string} storeName Store name the selector belongs to.
 * @return {Object} Object with keys as functions for each resolver in the given store.
 */
export const untilResolved = ( registry, storeName ) => {
	return mapValues(
		registry.stores[ storeName ].resolvers || {},
		( resolverFn, resolverName ) => ( ...args ) => {
			return subscribeUntil(
				registry,
				() => registry.select( storeName ).hasFinishedResolution( resolverName, args ),
			);
		},
	);
};

export const subscribeUntil = ( registry, predicates ) => {
	predicates = castArray( predicates );

	return new Promise( ( resolve ) => {
		subscribeWithUnsubscribe( registry, () => {
			if ( predicates.every( ( predicate ) => predicate() ) ) {
				resolve();
			}
		} );
	} );
};

export const unsubscribeFromAll = () => {
	let unsubscribe;
	while ( ( unsubscribe = unsubscribes.shift() ) ) {
		unsubscribe();
	}
};

/**
 * Returns a rejection.
 *
 * Used to ensure that a test will fail if it reaches this point.
 * Useful for asynchronous code that has multiple code paths, eg:
 *
 * ```js
 * try {
 *   await codeThatThrowsAnError();
 *   return unexpectedSuccess();
 * } catch (err) {
 *   expect(err.message).toEqual('Some error.');
 * }
 * ```
 *
 * Use this to ensure that the unintended path throws an error rather than
 * silently succeed.
 *
 * @since 1.5.0
 * @private
 *
 * @return {Promise} A rejected promise.
 */
export const unexpectedSuccess = () => {
	return Promise.reject( new Error(
		'Some code (likely a Promise) succeeded unexpectedly; check your test.',
	) );
};
<|endoftext|>


<commit_before>

var chai = require("chai");
var expect = chai.expect;
var RG = require("../battles.js");

var Actor = RG.Actor.Rogue;
var Level = RG.RogueLevel;

var updateSystems = function(systems) {
    for (var i = 0; i < systems.length; i++) {
        systems[i].update();
    }
};

describe('How dice are cast and what values they give', function() {
    it('Produces random values based on constructor arguments', function() {
        var die = new RG.Die(1, 10, 1);
        for (var i = 0; i < 100; i++) {
            var val = die.roll();
            expect(val >= 2).to.equal(true);
            expect(val <= 11).to.equal(true);
        }

        var factDie = RG.FACT.createDie("2d4 + 2");
        for (i = 0; i < 100; i++) {
            val = factDie.roll();
            expect(val >= 4).to.equal(true);
            expect(val <= 10).to.equal(true);
        }
    });

    it('Works also with string args', function() {
        var die = new RG.Die("1", "10", "1");
        for (var i = 0; i < 100; i++) {
            var val = die.roll();
            expect(val >= 2).to.equal(true);
            expect(val <= 11).to.equal(true);
        }

        var dieStr = die.toString();
        expect(dieStr).to.equal("1d10 + 1");

        var die2 = new RG.Die(1, 10, 1);
        expect(die2.equals(die)).to.equal(true);
        expect(die.equals(die2)).to.equal(true);

        var die3 = new RG.Die(0, 0, 0);
        for (var j = 0; j < 20; j++)
            expect(die3.roll()).to.equal(0);

        die3.copy(die);
        expect(die3.equals(die)).to.equal(true);
    });
});

describe('How missile is fired and hits a wall', function() {

    var mSystem = new RG.System.Missile("Missile", ["Missile"]);
    var dSystem = new RG.System.Damage("Damage", ["Damage"]);

    var systems = [mSystem, dSystem];

    it('Starts from source and flies to target', function() {
        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");

        level.addActor(srcEnt, 1, 1);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mEnt.add("Missile", mComp);

        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(1);
        mComp.setTargetXY(1, 4);
        mComp.setRange(3);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(4);
        expect(mComp.inTarget()).to.equal(true);
        expect(mComp.isFlying()).to.equal(false);

        // Now item should be lying around in the slot
        var targetCell = level.getMap().getCell(1, 4);
        expect(targetCell.hasProp("items")).to.equal(true);
    });

    it('Stops and hits a wall', function() {
        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);

        var wall = new RG.Element.Base("wall");
        var map = level.getMap();
        var cell = map.getCell(1, 3);
        cell.setProp("elements", wall);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 4);
        mComp.setRange(3);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(2);
        expect(mComp.inTarget()).to.equal(false);
        expect(mComp.isFlying()).to.equal(false);

        var targetCell = level.getMap().getCell(1, 2);
        expect(targetCell.hasProp("items")).to.equal(true);

    });

    it('Stops and hits an entity (actor)', function() {
        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);
        var targetEnt = new Actor("prey");
        var targetHP = targetEnt.get("Health").getHP();

        targetEnt.get("Combat").setDefense(0);
        level.addActor(targetEnt, 1, 6);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mComp.setAttack(1);
        mComp.setDamage(5);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 6);
        mComp.setRange(10);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(6);
        expect(mComp.inTarget()).to.equal(true);
        expect(mComp.isFlying()).to.equal(false);

        var currHP = targetEnt.get("Health").getHP();
        expect(targetEnt.has("Damage")).to.equal(false);
        expect(currHP).to.equal(targetHP - 5);

        var targetCell = level.getMap().getCell(1, 6);
        expect(targetCell.hasProp("items")).to.equal(true);
        expect(targetCell.hasPropType("missile")).to.equal(true);

    });

    it('Stops after reaching maximum range', function() {
        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mComp.setDamage(5);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 6);
        mComp.setRange(4);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(5);
        expect(mComp.inTarget()).to.equal(false);
        expect(mComp.isFlying()).to.equal(false);

        var targetCell = level.getMap().getCell(1, 5);
        expect(targetCell.hasProp("items")).to.equal(true);
        expect(targetCell.hasPropType("missile")).to.equal(true);
    });
});
<commit_msg>Moved system creation inside 'it' functions.<commit_after>

var chai = require("chai");
var expect = chai.expect;
var RG = require("../battles.js");

var Actor = RG.Actor.Rogue;
var Level = RG.RogueLevel;

var updateSystems = function(systems) {
    for (var i = 0; i < systems.length; i++) {
        systems[i].update();
    }
};

describe('How dice are cast and what values they give', function() {
    it('Produces random values based on constructor arguments', function() {
        var die = new RG.Die(1, 10, 1);
        for (var i = 0; i < 100; i++) {
            var val = die.roll();
            expect(val >= 2).to.equal(true);
            expect(val <= 11).to.equal(true);
        }

        var factDie = RG.FACT.createDie("2d4 + 2");
        for (i = 0; i < 100; i++) {
            val = factDie.roll();
            expect(val >= 4).to.equal(true);
            expect(val <= 10).to.equal(true);
        }
    });

    it('Works also with string args', function() {
        var die = new RG.Die("1", "10", "1");
        for (var i = 0; i < 100; i++) {
            var val = die.roll();
            expect(val >= 2).to.equal(true);
            expect(val <= 11).to.equal(true);
        }

        var dieStr = die.toString();
        expect(dieStr).to.equal("1d10 + 1");

        var die2 = new RG.Die(1, 10, 1);
        expect(die2.equals(die)).to.equal(true);
        expect(die.equals(die2)).to.equal(true);

        var die3 = new RG.Die(0, 0, 0);
        for (var j = 0; j < 20; j++)
            expect(die3.roll()).to.equal(0);

        die3.copy(die);
        expect(die3.equals(die)).to.equal(true);
    });
});

describe('How missile is fired and hits a wall', function() {

    it('Starts from source and flies to target', function() {
        var mSystem = new RG.System.Missile("Missile", ["Missile"]);
        var dSystem = new RG.System.Damage("Damage", ["Damage"]);

        var systems = [mSystem, dSystem];

        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");

        level.addActor(srcEnt, 1, 1);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mEnt.add("Missile", mComp);

        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(1);
        mComp.setTargetXY(1, 4);
        mComp.setRange(3);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(4);
        expect(mComp.inTarget()).to.equal(true);
        expect(mComp.isFlying()).to.equal(false);

        // Now item should be lying around in the slot
        var targetCell = level.getMap().getCell(1, 4);
        expect(targetCell.hasProp("items")).to.equal(true);
    });

    it('Stops and hits a wall', function() {
        var mSystem = new RG.System.Missile("Missile", ["Missile"]);
        var dSystem = new RG.System.Damage("Damage", ["Damage"]);
        var systems = [mSystem, dSystem];

        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);

        var wall = new RG.Element.Base("wall");
        var map = level.getMap();
        var cell = map.getCell(1, 3);
        cell.setProp("elements", wall);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 4);
        mComp.setRange(3);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(2);
        expect(mComp.inTarget()).to.equal(false);
        expect(mComp.isFlying()).to.equal(false);

        var targetCell = level.getMap().getCell(1, 2);
        expect(targetCell.hasProp("items")).to.equal(true);

    });

    it('Stops and hits an entity (actor)', function() {
        var mSystem = new RG.System.Missile("Missile", ["Missile"]);
        var dSystem = new RG.System.Damage("Damage", ["Damage"]);
        var systems = [mSystem, dSystem];

        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);
        var targetEnt = new Actor("prey");
        var targetHP = targetEnt.get("Health").getHP();

        targetEnt.get("Combat").setDefense(0);
        level.addActor(targetEnt, 1, 6);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mComp.setAttack(1);
        mComp.setDamage(5);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 6);
        mComp.setRange(10);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(6);
        expect(mComp.inTarget()).to.equal(true);
        expect(mComp.isFlying()).to.equal(false);

        var currHP = targetEnt.get("Health").getHP();
        expect(targetEnt.has("Damage")).to.equal(false);
        expect(currHP).to.equal(targetHP - 5);

        var targetCell = level.getMap().getCell(1, 6);
        expect(targetCell.hasProp("items")).to.equal(true);
        expect(targetCell.hasPropType("missile")).to.equal(true);

    });

    it('Stops after reaching maximum range', function() {
        var mSystem = new RG.System.Missile("Missile", ["Missile"]);
        var dSystem = new RG.System.Damage("Damage", ["Damage"]);
        var systems = [mSystem, dSystem];
        var level = RG.FACT.createLevel("arena", 30, 30);
        // Archer to fire the missiles
        var srcEnt = new Actor("archer");
        level.addActor(srcEnt, 1, 1);

        var mEnt = new RG.Item.Missile("missile");
        var mComp = new RG.Component.Missile(srcEnt);
        mComp.setDamage(5);
        mEnt.add("Missile", mComp);
        mComp.setTargetXY(1, 6);
        mComp.setRange(4);

        updateSystems(systems);
        expect(mComp.getX()).to.equal(1);
        expect(mComp.getY()).to.equal(5);
        expect(mComp.inTarget()).to.equal(false);
        expect(mComp.isFlying()).to.equal(false);

        var targetCell = level.getMap().getCell(1, 5);
        expect(targetCell.hasProp("items")).to.equal(true);
        expect(targetCell.hasPropType("missile")).to.equal(true);
    });
});
<|endoftext|>


<commit_before>describe("DC/OS UI [00j]", function() {
  beforeEach(function() {
    cy.configureCluster({
      mesos: "1-task-healthy"
    }).visitUrl({ url: "/", identify: true, fakeAnalytics: true });
  });

  context("Dashboard [00k]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Dashboard")
        .click();
    });

    it("can change hash to dashboard page [00l]", function() {
      cy.hash().should("match", /dashboard/);
    });

    it("has eight panels [00m]", function() {
      cy.get("#application")
        .find(".panel")
        .should("to.have.length", 8);
    });
  });

  xcontext("Services [00n]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Services")
        .click();
      cy.get("table tbody tr").as("tableRows");
    });

    it("can change hash to services page [00o]", function() {
      cy.hash().should("match", /services/);
    });

    it("displays one row on the table [00p]", function() {
      cy.get("@tableRows").should("to.have.length", 3);
    });

    it("doesn't list marathon in the table [00q]", function() {
      cy.get("table tbody tr").should("not.contain", "marathon");
    });
  });

  context("Nodes [00r]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Nodes")
        .click();
    });

    it("can change hash to nodes page [00s]", function() {
      cy.hash().should("match", /nodes/);
    });

    it("displays one row on the table [00t]", function() {
      cy.get("table tbody tr")
        .should("to.have.length", 6)
        .contains("dcos-01");
    });
  });
});
<commit_msg>test(smoke): add force because sidebar overlaps somehow<commit_after>describe("DC/OS UI [00j]", function() {
  beforeEach(function() {
    cy.configureCluster({
      mesos: "1-task-healthy"
    }).visitUrl({ url: "/", identify: true, fakeAnalytics: true });
  });

  context("Dashboard [00k]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Dashboard")
        .click({ force: true });
    });

    it("can change hash to dashboard page [00l]", function() {
      cy.hash().should("match", /dashboard/);
    });

    it("has eight panels [00m]", function() {
      cy.get("#application")
        .find(".panel")
        .should("to.have.length", 8);
    });
  });

  xcontext("Services [00n]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Services")
        .click();
      cy.get("table tbody tr").as("tableRows");
    });

    it("can change hash to services page [00o]", function() {
      cy.hash().should("match", /services/);
    });

    it("displays one row on the table [00p]", function() {
      cy.get("@tableRows").should("to.have.length", 3);
    });

    it("doesn't list marathon in the table [00q]", function() {
      cy.get("table tbody tr").should("not.contain", "marathon");
    });
  });

  context("Nodes [00r]", function() {
    beforeEach(function() {
      cy.get(".sidebar-menu-item")
        .contains("Nodes")
        .click({ force: true });
    });

    it("can change hash to nodes page [00s]", function() {
      cy.hash().should("match", /nodes/);
    });

    it("displays one row on the table [00t]", function() {
      cy.get(
        ".BottomLeftGrid_ScrollWrapper .ReactVirtualized__Grid__innerScrollContainer a"
      )
        .should("to.have.length", 4)
        .contains("dcos-01");
    });
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, describe, beforeEach, afterEach, it, runs, waitsFor, expect, brackets, waitsForDone */

define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var CommandManager,      // loaded from brackets.test
        Commands,            // loaded from brackets.test
        EditorManager,       // loaded from brackets.test
        DocumentManager,     // loaded from brackets.test
        FileViewController,
        SpecRunnerUtils     = require("spec/SpecRunnerUtils");

    describe("EditorOptionHandlers", function () {
        this.category = "integration";

        var testPath = SpecRunnerUtils.getTestPath("/spec/EditorOptionHandlers-test-files"),
            testWindow;

        beforeEach(function () {
            SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                testWindow = w;

                // Load module instances from brackets.test
                CommandManager      = testWindow.brackets.test.CommandManager;
                Commands            = testWindow.brackets.test.Commands;
                EditorManager       = testWindow.brackets.test.EditorManager;
                DocumentManager     = testWindow.brackets.test.DocumentManager;
                FileViewController  = testWindow.brackets.test.FileViewController;

                SpecRunnerUtils.loadProjectInTestWindow(testPath);
            });
        });

        afterEach(function () {
            SpecRunnerUtils.closeTestWindow();
        });

        function checkLineWrapping(firstPos, secondPos, shouldWrap, inlineEditor) {
            runs(function () {
                var firstLineBottom,
                    nextLineBottom,
                    editor = inlineEditor || EditorManager.getCurrentFullEditor();

                expect(editor).toBeTruthy();

                editor.setCursorPos(firstPos);
                firstLineBottom = editor._codeMirror.cursorCoords(null, "local").bottom;

                editor.setCursorPos(secondPos);
                nextLineBottom = editor._codeMirror.cursorCoords(null, "local").bottom;
                if (shouldWrap) {
                    expect(firstLineBottom).toBeLessThan(nextLineBottom);
                } else {
                    expect(firstLineBottom).toEqual(nextLineBottom);
                }
            });
        }

        var CSS_FILE  = testPath + "/test.css",
            HTML_FILE = testPath + "/test.html";

        it("should wrap long lines in main editor by default", function () {
            var promise,
                editor;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: HTML_FILE});
                waitsForDone(promise, "Open into working set");

                // Use two cursor positions to detect line wrapping. First position at
                // the beginning of a long line and the second position to be
                // somewhere on the long line that will be part of an extra line
                // created by word-wrap and get its bottom coordinate.
                checkLineWrapping({line: 8, ch: 0}, {line: 8, ch: 210}, true);
            });
        });

        it("should also wrap long lines in inline editor by default", function () {
            var promise,
                inlineEditor;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: HTML_FILE});
                waitsForDone(promise, "Open into working set");
            });

            runs(function () {
                // Open inline editor onto test.css's ".testClass" rule
                promise = SpecRunnerUtils.toggleQuickEditAtOffset(EditorManager.getCurrentFullEditor(), {line: 8, ch: 11});
                waitsForDone(promise, "Open inline editor");
            });

            runs(function () {
                inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editors[0];
                expect(inlineEditor).toBeTruthy();

                checkLineWrapping({line: 0, ch: 0}, {line: 0, ch: 160}, true, inlineEditor);
            });
        });

        it("should NOT wrap the long lines after turning off word-wrap", function () {
            var promise,
                editor;

            // Turn off word-wrap
            runs(function () {
                promise = CommandManager.execute(Commands.TOGGLE_WORD_WRAP);
                waitsForDone(promise, "Toggle word-wrap");
            });

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: CSS_FILE});
                waitsForDone(promise, "Open into working set");
                checkLineWrapping({line: 0, ch: 1}, {line: 0, ch: 180}, false);
            });
        });

        it("should NOT wrap the long lines in another document when word-wrap off", function () {
            var promise,
                editor,
                firstLineBottom,
                nextLineBottom;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: CSS_FILE});
                waitsForDone(promise, "Open into working set");
            });

            // Turn off word-wrap
            runs(function () {
                promise = CommandManager.execute(Commands.TOGGLE_WORD_WRAP);
                waitsForDone(promise, "Toggle word-wrap");
            });

            runs(function () {
                // Open another document and bring it to the front
                waitsForDone(FileViewController.openAndSelectDocument(HTML_FILE, FileViewController.PROJECT_MANAGER),
                             "FILE_OPEN on file timeout", 1000);
                checkLineWrapping({line: 8, ch: 0}, {line: 8, ch: 210}, false);
            });
        });

        it("should show active line in main editor by default", function () {
            var promise,
                editor,
                lineInfo;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: HTML_FILE});
                waitsForDone(promise, "Open into working set");
            });

            runs(function () {
                editor = EditorManager.getCurrentFullEditor();
                expect(editor).toBeTruthy();

                editor.setCursorPos({line: 5, ch: 0});
                lineInfo = editor._codeMirror.lineInfo(5);
                expect(lineInfo.wrapClass).toBe("CodeMirror-activeline");
            });
        });

        it("should also show active line in inline editor by default", function () {
            var promise,
                inlineEditor,
                lineInfo;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: HTML_FILE});
                waitsForDone(promise, "Open into working set");
            });

            runs(function () {
                // Open inline editor onto test.css's ".testClass" rule
                promise = SpecRunnerUtils.toggleQuickEditAtOffset(EditorManager.getCurrentFullEditor(), {line: 8, ch: 11});
                waitsForDone(promise, "Open inline editor");
            });

            runs(function () {
                inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editors[0];
                expect(inlineEditor).toBeTruthy();

                lineInfo = inlineEditor._codeMirror.lineInfo(0);
                expect(lineInfo.wrapClass).toBe("CodeMirror-activeline");
            });
        });

        it("should NOT style active line after turning it off", function () {
            var promise,
                editor,
                lineInfo;

            // Turn off show active line
            runs(function () {
                promise = CommandManager.execute(Commands.TOGGLE_ACTIVE_LINE);
                waitsForDone(promise, "Toggle active line");
            });

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: CSS_FILE});
                waitsForDone(promise, "Open into working set");
            });

            runs(function () {
                editor = EditorManager.getCurrentFullEditor();
                expect(editor).toBeTruthy();

                lineInfo = editor._codeMirror.lineInfo(0);
                expect(lineInfo.wrapClass).toBeUndefined();
            });
        });

        it("should NOT style the active line when opening another document with show active line off", function () {
            var promise,
                editor,
                lineInfo;

            runs(function () {
                promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: CSS_FILE});
                waitsForDone(promise, "Open into working set");
            });

            // Turn off show active line
            runs(function () {
                promise = CommandManager.execute(Commands.TOGGLE_ACTIVE_LINE);
                waitsForDone(promise, "Toggle active line");
            });

            runs(function () {
                // Open another document and bring it to the front
                waitsForDone(FileViewController.openAndSelectDocument(HTML_FILE, FileViewController.PROJECT_MANAGER),
                             "FILE_OPEN on file timeout", 1000);
            });

            runs(function () {
                editor = EditorManager.getCurrentFullEditor();
                expect(editor).toBeTruthy();

                editor.setCursorPos({line: 3, ch: 5});
                lineInfo = editor._codeMirror.lineInfo(3);
                expect(lineInfo.wrapClass).toBeUndefined();
            });
        });
    });
});
<commit_msg>New show line numbers tests and code refactor<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, describe, beforeEach, afterEach, it, runs, waitsFor, expect, brackets, waitsForDone */

define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var CommandManager,      // loaded from brackets.test
        Commands,            // loaded from brackets.test
        EditorManager,       // loaded from brackets.test
        DocumentManager,     // loaded from brackets.test
        FileViewController,
        SpecRunnerUtils     = require("spec/SpecRunnerUtils");

    describe("EditorOptionHandlers", function () {
        this.category = "integration";

        var testPath = SpecRunnerUtils.getTestPath("/spec/EditorOptionHandlers-test-files"),
            testWindow;

        var CSS_FILE  = testPath + "/test.css",
            HTML_FILE = testPath + "/test.html";

        beforeEach(function () {
            SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                testWindow = w;

                // Load module instances from brackets.test
                CommandManager      = testWindow.brackets.test.CommandManager;
                Commands            = testWindow.brackets.test.Commands;
                EditorManager       = testWindow.brackets.test.EditorManager;
                DocumentManager     = testWindow.brackets.test.DocumentManager;
                FileViewController  = testWindow.brackets.test.FileViewController;

                SpecRunnerUtils.loadProjectInTestWindow(testPath);
            });
        });

        afterEach(function () {
            SpecRunnerUtils.closeTestWindow();
        });

        function checkLineWrapping(firstPos, secondPos, shouldWrap, inlineEditor) {
            runs(function () {
                var firstLineBottom,
                    nextLineBottom,
                    editor = inlineEditor || EditorManager.getCurrentFullEditor();

                expect(editor).toBeTruthy();

                editor.setCursorPos(firstPos);
                firstLineBottom = editor._codeMirror.cursorCoords(null, "local").bottom;

                editor.setCursorPos(secondPos);
                nextLineBottom = editor._codeMirror.cursorCoords(null, "local").bottom;
                if (shouldWrap) {
                    expect(firstLineBottom).toBeLessThan(nextLineBottom);
                } else {
                    expect(firstLineBottom).toEqual(nextLineBottom);
                }
            });
        }

        // Helper functions to open editors / toggle options
        function openEditor(fullPath) {
            runs(function () {
                var promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, {fullPath: fullPath});
                waitsForDone(promise, "Open into working set");
            });
        }
        function openAnotherEditor(fullpath) {
            runs(function () {
                // Open another document and bring it to the front
                waitsForDone(FileViewController.openAndSelectDocument(fullpath, FileViewController.PROJECT_MANAGER),
                             "FILE_OPEN on file timeout", 1000);
            });
        }
        function openInlineEditor() {
            openEditor(HTML_FILE);

            runs(function () {
                // Open inline editor onto test.css's ".testClass" rule
                var promise = SpecRunnerUtils.toggleQuickEditAtOffset(EditorManager.getCurrentFullEditor(), {line: 8, ch: 11});
                waitsForDone(promise, "Open inline editor");
            });
        }
        function toggleOption(commandID, text) {
            runs(function () {
                var promise = CommandManager.execute(commandID);
                waitsForDone(promise, text);
            });
        }

        describe("Toggle Word Wrap", function () {
            it("should wrap long lines in main editor by default", function () {
                openEditor(HTML_FILE);

                runs(function () {
                    // Use two cursor positions to detect line wrapping. First position at
                    // the beginning of a long line and the second position to be
                    // somewhere on the long line that will be part of an extra line
                    // created by word-wrap and get its bottom coordinate.
                    checkLineWrapping({line: 8, ch: 0}, {line: 8, ch: 210}, true);
                });
            });

            it("should also wrap long lines in inline editor by default", function () {
                var inlineEditor;

                openInlineEditor();

                runs(function () {
                    inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editors[0];
                    expect(inlineEditor).toBeTruthy();

                    checkLineWrapping({line: 0, ch: 0}, {line: 0, ch: 160}, true, inlineEditor);
                });
            });

            it("should NOT wrap the long lines after turning off word-wrap", function () {
                // Turn off word-wrap
                toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap");

                openEditor(CSS_FILE);

                runs(function () {
                    checkLineWrapping({line: 0, ch: 1}, {line: 0, ch: 180}, false);
                });
            });

            it("should NOT wrap the long lines in another document when word-wrap off", function () {
                openEditor(CSS_FILE);

                // Turn off word-wrap
                toggleOption(Commands.TOGGLE_WORD_WRAP, "Toggle word-wrap");

                openAnotherEditor(HTML_FILE);

                runs(function () {
                    checkLineWrapping({line: 8, ch: 0}, {line: 8, ch: 210}, false);
                });
            });
        });

        describe("Toggle Active Line", function () {
            it("should show active line in main editor by default", function () {
                var editor, lineInfo;

                openEditor(HTML_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    editor.setCursorPos({line: 5, ch: 0});
                    lineInfo = editor._codeMirror.lineInfo(5);
                    expect(lineInfo.wrapClass).toBe("CodeMirror-activeline");
                });
            });

            it("should also show active line in inline editor by default", function () {
                var inlineEditor, lineInfo;

                openInlineEditor();

                runs(function () {
                    inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editors[0];
                    expect(inlineEditor).toBeTruthy();

                    lineInfo = inlineEditor._codeMirror.lineInfo(0);
                    expect(lineInfo.wrapClass).toBe("CodeMirror-activeline");
                });
            });

            it("should NOT style active line after turning it off", function () {
                var editor, lineInfo;

                // Turn off show active line
                toggleOption(Commands.TOGGLE_ACTIVE_LINE, "Toggle active line");

                openEditor(CSS_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    lineInfo = editor._codeMirror.lineInfo(0);
                    expect(lineInfo.wrapClass).toBeUndefined();
                });
            });

            it("should NOT style the active line when opening another document with show active line off", function () {
                var editor, lineInfo;

                openEditor(CSS_FILE);

                // Turn off show active line
                toggleOption(Commands.TOGGLE_ACTIVE_LINE, "Toggle active line");

                openAnotherEditor(HTML_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    editor.setCursorPos({line: 3, ch: 5});
                    lineInfo = editor._codeMirror.lineInfo(3);
                    expect(lineInfo.wrapClass).toBeUndefined();
                });
            });
        });

        describe("Toggle Line Numbers", function () {
            it("should show line numbers in main editor by default", function () {
                var editor, gutterElement;

                openEditor(HTML_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    gutterElement = editor._codeMirror.getGutterElement();
                    expect(gutterElement.style.display).toBe("");
                });
            });

            it("should also show line numbers in inline editor by default", function () {
                var inlineEditor, gutterElement;

                openInlineEditor();

                runs(function () {
                    inlineEditor = EditorManager.getCurrentFullEditor().getInlineWidgets()[0].editors[0];
                    expect(inlineEditor).toBeTruthy();

                    gutterElement = inlineEditor._codeMirror.getGutterElement();
                    expect(gutterElement.style.display).toBe("");
                });
            });

            it("should NOT show line numbers after turning it off", function () {
                var editor, gutterElement;

                // Turn off show line numbers
                toggleOption(Commands.TOGGLE_LINE_NUMBERS, "Toggle line numbers");

                openEditor(CSS_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    gutterElement = editor._codeMirror.getGutterElement();
                    expect(gutterElement.style.display).toBe("none");
                });
            });

            it("should NOT show line numbers when opening another document with show line numbers off", function () {
                var editor, gutterElement;

                openEditor(CSS_FILE);

                // Turn off show line numbers
                toggleOption(Commands.TOGGLE_LINE_NUMBERS, "Toggle line numbers");

                openAnotherEditor(HTML_FILE);

                runs(function () {
                    editor = EditorManager.getCurrentFullEditor();
                    expect(editor).toBeTruthy();

                    gutterElement = editor._codeMirror.getGutterElement();
                    expect(gutterElement.style.display).toBe("none");
                });
            });
        });
    });
});
<|endoftext|>