<commit_before>// Eventually, require cdvtest plugin, for now its global

registerAutoTests('org.apache.cordova.contacts', function() {
  // global to store a contact so it doesn't have to be created or retrieved multiple times
  // all of the setup/teardown test methods can reference the following variables to make sure to do the right cleanup
  var gContactObj = null;
  var gContactId = null;

  var fail = function(done) {
    expect(true).toBe(false);
    done();
  };

  var removeContact = function(){
      if (gContactObj) {
          gContactObj.remove(function(){},function(){
              console.log("[CONTACTS ERROR]: removeContact cleanup method failed to clean up test artifacts.");
          });
          gContactObj = null;
      }
  };

  describe("Contacts (navigator.contacts)", function () {
      it("contacts.spec.1 should exist", function() {
          expect(navigator.contacts).toBeDefined();
      });
      it("contacts.spec.2 should contain a find function", function() {
          expect(navigator.contacts.find).toBeDefined();
          expect(typeof navigator.contacts.find).toBe('function');
      });
      describe("find method", function() {
          it("contacts.spec.3 success callback should be called with an array", function(done) {
              var win = function(result) {
                      expect(result).toBeDefined();
                      expect(result instanceof Array).toBe(true);
                      done();
                  },
                  obj = new ContactFindOptions();

              obj.filter="";
              obj.multiple=true;
              navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, fail.bind(null, done), obj);
          });
          it("contacts.spec.4 should throw an exception if success callback is empty", function() {
              var obj = new ContactFindOptions();
              obj.filter="";
              obj.multiple=true;

              expect(function () {
                  navigator.contacts.find(["displayName", "name", "emails", "phoneNumbers"], null, fail.bind(null, done), obj);
              }).toThrow();
          });
          it("contacts.spec.5 error callback should be called when no fields are specified", function(done) {
              var win = fail, // we don't want this to be called
                  error = function(result) {
                      expect(result).toBeDefined();
                      expect(result.code).toBe(ContactError.INVALID_ARGUMENT_ERROR);
                      done();
                  },
                  obj = new ContactFindOptions();

              obj.filter="";
              obj.multiple=true;
              navigator.contacts.find([], win, error, obj);
          });
          describe("with newly-created contact", function () {

              afterEach(removeContact);

              it("contacts.spec.6 should be able to find a contact by name", function(done) {
                  var foundName = function(result) {
                          var bFound = false;
                          try {
                              for (var i=0; i < result.length; i++) {
                                  if (result[i].name.familyName == "Delete") {
                                      bFound = true;
                                      break;
                                  }
                              }
                          } catch(e) {
                              return false;
                          }
                          return bFound;
                      },
                      test = function(savedContact) {
                          // update so contact will get removed
                          gContactObj = savedContact;
                          // ----
                          // Find asserts
                          // ---
                          var findWin = function(object) {
                                  console.log('in findwin');
                                  expect(object instanceof Array).toBe(true);
                                  expect(object.length >= 1).toBe(true);
                                  expect(foundName(object)).toBe(true);
                                  done();
                              },
                              findFail = fail,
                              obj = new ContactFindOptions();

                          obj.filter="Delete";
                          obj.multiple=true;

                          navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail.bind(null, done), obj);
                      };

                  gContactObj = new Contact();
                  gContactObj.name = new ContactName();
                  gContactObj.name.familyName = "Delete";
                  gContactObj.save(test, fail.bind(null, done));
              });
          });

      });
      describe('create method', function() {
          it("contacts.spec.1 should exist", function() {
              expect(navigator.contacts.create).toBeDefined();
              expect(typeof navigator.contacts.create).toBe('function');
          });
          it("contacts.spec.8 should return a Contact object", function() {
              var bDay = new Date(1976, 7,4);
              var obj = navigator.contacts.create({"displayName": "test name", "gender": "male", "note": "my note", "name": {"formatted": "Mr. Test Name"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});

              expect(obj).toBeDefined();
              expect(obj.displayName).toBe('test name');
              expect(obj.note).toBe('my note');
              expect(obj.name.formatted).toBe('Mr. Test Name');
              expect(obj.emails.length).toBe(2);
              expect(obj.emails[0].value).toBe('here@there.com');
              expect(obj.emails[1].value).toBe('there@here.com');
              expect(obj.nickname).toBe(null);
              expect(obj.birthday).toBe(bDay);
          });
      });

      describe("Contact object", function () {
          it("contacts.spec.9 should be able to create instance", function() {
              var contact = new Contact("a", "b", new ContactName("a", "b", "c", "d", "e", "f"), "c", [], [], [], [], [], "f", "i",
                  [], [], []);
              expect(contact).toBeDefined();
              expect(contact.id).toBe("a");
              expect(contact.displayName).toBe("b");
              expect(contact.name.formatted).toBe("a");
              expect(contact.nickname).toBe("c");
              expect(contact.phoneNumbers).toBeDefined();
              expect(contact.emails).toBeDefined();
              expect(contact.addresses).toBeDefined();
              expect(contact.ims).toBeDefined();
              expect(contact.organizations).toBeDefined();
              expect(contact.birthday).toBe("f");
              expect(contact.note).toBe("i");
              expect(contact.photos).toBeDefined();
              expect(contact.categories).toBeDefined();
              expect(contact.urls).toBeDefined();
          });
          it("contacts.spec.10 should be able to define a ContactName object", function() {
              var contactName = new ContactName("Dr. First Last Jr.", "Last", "First", "Middle", "Dr.", "Jr.");
              expect(contactName).toBeDefined();
              expect(contactName.formatted).toBe("Dr. First Last Jr.");
              expect(contactName.familyName).toBe("Last");
              expect(contactName.givenName).toBe("First");
              expect(contactName.middleName).toBe("Middle");
              expect(contactName.honorificPrefix).toBe("Dr.");
              expect(contactName.honorificSuffix).toBe("Jr.");
          });
          it("contacts.spec.11 should be able to define a ContactField object", function() {
              var contactField = new ContactField("home", "8005551212", true);
              expect(contactField).toBeDefined();
              expect(contactField.type).toBe("home");
              expect(contactField.value).toBe("8005551212");
              expect(contactField.pref).toBe(true);
          });
          it("contacts.spec.12 ContactField object should coerce type and value properties to strings", function() {
              var contactField = new ContactField(12345678, 12345678, true);
              expect(contactField.type).toBe("12345678");
              expect(contactField.value).toBe("12345678");
          });
          it("contacts.spec.13 should be able to define a ContactAddress object", function() {
              var contactAddress = new ContactAddress(true, "home", "a","b","c","d","e","f");
              expect(contactAddress).toBeDefined();
              expect(contactAddress.pref).toBe(true);
              expect(contactAddress.type).toBe("home");
              expect(contactAddress.formatted).toBe("a");
              expect(contactAddress.streetAddress).toBe("b");
              expect(contactAddress.locality).toBe("c");
              expect(contactAddress.region).toBe("d");
              expect(contactAddress.postalCode).toBe("e");
              expect(contactAddress.country).toBe("f");
          });
          it("contacts.spec.14 should be able to define a ContactOrganization object", function() {
              var contactOrg = new ContactOrganization(true, "home", "a","b","c","d","e","f","g");
              expect(contactOrg).toBeDefined();
              expect(contactOrg.pref).toBe(true);
              expect(contactOrg.type).toBe("home");
              expect(contactOrg.name).toBe("a");
              expect(contactOrg.department).toBe("b");
              expect(contactOrg.title).toBe("c");
          });
          it("contacts.spec.15 should be able to define a ContactFindOptions object", function() {
              var contactFindOptions = new ContactFindOptions("a", true, "b");
              expect(contactFindOptions).toBeDefined();
              expect(contactFindOptions.filter).toBe("a");
              expect(contactFindOptions.multiple).toBe(true);
          });
          it("contacts.spec.16 should contain a clone function", function() {
              var contact = new Contact();
              expect(contact.clone).toBeDefined();
              expect(typeof contact.clone).toBe('function');
          });
          it("contacts.spec.17 clone function should make deep copy of Contact Object", function() {
              var contact = new Contact();
              contact.id=1;
              contact.displayName="Test Name";
              contact.nickname="Testy";
              contact.gender="male";
              contact.note="note to be cloned";
              contact.name = new ContactName("Mr. Test Name");

              var clonedContact = contact.clone();

              expect(contact.id).toBe(1);
              expect(clonedContact.id).toBe(null);
              expect(clonedContact.displayName).toBe(contact.displayName);
              expect(clonedContact.nickname).toBe(contact.nickname);
              expect(clonedContact.gender).toBe(contact.gender);
              expect(clonedContact.note).toBe(contact.note);
              expect(clonedContact.name.formatted).toBe(contact.name.formatted);
              expect(clonedContact.connected).toBe(contact.connected);
          });
          it("contacts.spec.18 should contain a save function", function() {
              var contact = new Contact();
              expect(contact.save).toBeDefined();
              expect(typeof contact.save).toBe('function');
          });
          it("contacts.spec.19 should contain a remove function", function() {
              var contact = new Contact();
              expect(contact.remove).toBeDefined();
              expect(typeof contact.remove).toBe('function');
          });
      });
      describe('save method', function () {
          it("contacts.spec.20 should be able to save a contact", function(done) {
              var bDay = new Date(1976, 6,4);
              gContactObj = navigator.contacts.create({"gender": "male", "note": "my note", "name": {"familyName": "Delete", "givenName": "Test"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});

              var saveSuccess = function(obj) {
                      expect(obj).toBeDefined();
                      expect(obj.note).toBe('my note');
                      expect(obj.name.familyName).toBe('Delete');
                      expect(obj.name.givenName).toBe('Test');
                      expect(obj.emails.length).toBe(2);
                      expect(obj.emails[0].value).toBe('here@there.com');
                      expect(obj.emails[1].value).toBe('there@here.com');
                      expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
                      expect(obj.addresses).toBe(null);
                      // must store returned object in order to have id for update test below
                      gContactObj = obj;
                      done();
                  },
                  saveFail = fail;

              gContactObj.save(saveSuccess, saveFail);
           });
          // HACK: there is a reliance between the previous and next test. This is bad form.
          it("contacts.spec.21 update a contact", function(done) {
              expect(gContactObj).toBeDefined();

              var bDay = new Date(1975, 5,4);
              var noteText = "an UPDATED note";

              var win = function(obj) {
                      expect(obj).toBeDefined();
                      expect(obj.id).toBe(gContactObj.id);
                      expect(obj.note).toBe(noteText);
                      expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
                      expect(obj.emails.length).toBe(1);
                      expect(obj.emails[0].value).toBe('here@there.com');
                      removeContact();         // Clean up contact object
                      done();
                  }, fail = function() { removeContact(); fail(done); };

              // remove an email
              gContactObj.emails[1].value = "";
              // change birthday
              gContactObj.birthday = bDay;
              // update note
              gContactObj.note = noteText;
              gContactObj.save(win, fail);
          });
      });
      describe('Contact.remove method', function (done) {
          afterEach(removeContact);

          it("contacts.spec.22 calling remove on a contact has an id of null should return ContactError.UNKNOWN_ERROR", function(done) {
              var win = function() {};
              var fail = function(result) {
                  expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
                  done();
              };

              var rmContact = new Contact();
              rmContact.remove(win, fail);
          });
          it("contacts.spec.23 calling remove on a contact that does not exist should return ContactError.UNKNOWN_ERROR", function(done) {
              var rmWin = fail;
              var rmFail = function(result) {
                  expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
                  done();
              };

              var rmContact = new Contact();
              // this is a bit risky as some devices may have contact ids that large
              var contact = new Contact("this string is supposed to be a unique identifier that will never show up on a device");
              contact.remove(rmWin, rmFail);
          });
      });
      describe("Round trip Contact tests (creating + save + delete + find).", function () {
          afterEach(removeContact);

          it("contacts.spec.24 Creating, saving, finding a contact should work, removing it should work, after which we should not be able to find it, and we should not be able to delete it again.", function(done) {
              gContactObj = new Contact();
              gContactObj.name = new ContactName();
              gContactObj.name.familyName = "DeleteMe";
              gContactObj.save(function(c_obj) {
                  var findWin = function(cs) {
                      expect(cs.length).toBe(1);
                      // update to have proper saved id
                      gContactObj = cs[0];
                      gContactObj.remove(function() {
                          var findWinAgain = function(seas) {
                              expect(seas.length).toBe(0);
                              gContactObj.remove(function() {
                                  throw("success callback called after non-existent Contact object called remove(). Test failed.");
                              }, function(e) {
                                  expect(e.code).toBe(ContactError.UNKNOWN_ERROR);
                                  done();
                              });
                          };
                          var findFailAgain = function(e) {
                              throw("find error callback invoked after delete, test failed.");
                          };
                          var obj = new ContactFindOptions();
                          obj.filter="DeleteMe";
                          obj.multiple=true;
                          navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWinAgain, findFailAgain, obj);
                      }, function(e) {
                          throw("Newly created contact's remove function invoked error callback. Test failed.");
                      });
                  };
                  var findFail = fail;
                  var obj = new ContactFindOptions();
                  obj.filter="DeleteMe";
                  obj.multiple=true;
                  navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
              }, fail);
          });
      });
      describe('ContactError interface', function () {
          it("contacts.spec.25 ContactError constants should be defined", function() {
              expect(ContactError.UNKNOWN_ERROR).toBe(0);
              expect(ContactError.INVALID_ARGUMENT_ERROR).toBe(1);
              expect(ContactError.TIMEOUT_ERROR).toBe(2);
              expect(ContactError.PENDING_OPERATION_ERROR).toBe(3);
              expect(ContactError.IO_ERROR).toBe(4);
              expect(ContactError.NOT_SUPPORTED_ERROR).toBe(5);
              expect(ContactError.PERMISSION_DENIED_ERROR).toBe(20);
          });
      });
  });
});
<commit_msg>Changing cdvtest format to use module exports<commit_after>exports.defineAutoTests = function() {
  // global to store a contact so it doesn't have to be created or retrieved multiple times
  // all of the setup/teardown test methods can reference the following variables to make sure to do the right cleanup
  var gContactObj = null;
  var gContactId = null;

  var fail = function(done) {
    expect(true).toBe(false);
    done();
  };

  var removeContact = function(){
      if (gContactObj) {
          gContactObj.remove(function(){},function(){
              console.log("[CONTACTS ERROR]: removeContact cleanup method failed to clean up test artifacts.");
          });
          gContactObj = null;
      }
  };

  describe("Contacts (navigator.contacts)", function () {
      it("contacts.spec.1 should exist", function() {
          expect(navigator.contacts).toBeDefined();
      });
      it("contacts.spec.2 should contain a find function", function() {
          expect(navigator.contacts.find).toBeDefined();
          expect(typeof navigator.contacts.find).toBe('function');
      });
      describe("find method", function() {
          it("contacts.spec.3 success callback should be called with an array", function(done) {
              var win = function(result) {
                      expect(result).toBeDefined();
                      expect(result instanceof Array).toBe(true);
                      done();
                  },
                  obj = new ContactFindOptions();

              obj.filter="";
              obj.multiple=true;
              navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, fail.bind(null, done), obj);
          });
          it("contacts.spec.4 should throw an exception if success callback is empty", function() {
              var obj = new ContactFindOptions();
              obj.filter="";
              obj.multiple=true;

              expect(function () {
                  navigator.contacts.find(["displayName", "name", "emails", "phoneNumbers"], null, fail.bind(null, done), obj);
              }).toThrow();
          });
          it("contacts.spec.5 error callback should be called when no fields are specified", function(done) {
              var win = fail, // we don't want this to be called
                  error = function(result) {
                      expect(result).toBeDefined();
                      expect(result.code).toBe(ContactError.INVALID_ARGUMENT_ERROR);
                      done();
                  },
                  obj = new ContactFindOptions();

              obj.filter="";
              obj.multiple=true;
              navigator.contacts.find([], win, error, obj);
          });
          describe("with newly-created contact", function () {

              afterEach(removeContact);

              it("contacts.spec.6 should be able to find a contact by name", function(done) {
                  var foundName = function(result) {
                          var bFound = false;
                          try {
                              for (var i=0; i < result.length; i++) {
                                  if (result[i].name.familyName == "Delete") {
                                      bFound = true;
                                      break;
                                  }
                              }
                          } catch(e) {
                              return false;
                          }
                          return bFound;
                      },
                      test = function(savedContact) {
                          // update so contact will get removed
                          gContactObj = savedContact;
                          // ----
                          // Find asserts
                          // ---
                          var findWin = function(object) {
                                  console.log('in findwin');
                                  expect(object instanceof Array).toBe(true);
                                  expect(object.length >= 1).toBe(true);
                                  expect(foundName(object)).toBe(true);
                                  done();
                              },
                              findFail = fail,
                              obj = new ContactFindOptions();

                          obj.filter="Delete";
                          obj.multiple=true;

                          navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail.bind(null, done), obj);
                      };

                  gContactObj = new Contact();
                  gContactObj.name = new ContactName();
                  gContactObj.name.familyName = "Delete";
                  gContactObj.save(test, fail.bind(null, done));
              });
          });

      });
      describe('create method', function() {
          it("contacts.spec.1 should exist", function() {
              expect(navigator.contacts.create).toBeDefined();
              expect(typeof navigator.contacts.create).toBe('function');
          });
          it("contacts.spec.8 should return a Contact object", function() {
              var bDay = new Date(1976, 7,4);
              var obj = navigator.contacts.create({"displayName": "test name", "gender": "male", "note": "my note", "name": {"formatted": "Mr. Test Name"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});

              expect(obj).toBeDefined();
              expect(obj.displayName).toBe('test name');
              expect(obj.note).toBe('my note');
              expect(obj.name.formatted).toBe('Mr. Test Name');
              expect(obj.emails.length).toBe(2);
              expect(obj.emails[0].value).toBe('here@there.com');
              expect(obj.emails[1].value).toBe('there@here.com');
              expect(obj.nickname).toBe(null);
              expect(obj.birthday).toBe(bDay);
          });
      });

      describe("Contact object", function () {
          it("contacts.spec.9 should be able to create instance", function() {
              var contact = new Contact("a", "b", new ContactName("a", "b", "c", "d", "e", "f"), "c", [], [], [], [], [], "f", "i",
                  [], [], []);
              expect(contact).toBeDefined();
              expect(contact.id).toBe("a");
              expect(contact.displayName).toBe("b");
              expect(contact.name.formatted).toBe("a");
              expect(contact.nickname).toBe("c");
              expect(contact.phoneNumbers).toBeDefined();
              expect(contact.emails).toBeDefined();
              expect(contact.addresses).toBeDefined();
              expect(contact.ims).toBeDefined();
              expect(contact.organizations).toBeDefined();
              expect(contact.birthday).toBe("f");
              expect(contact.note).toBe("i");
              expect(contact.photos).toBeDefined();
              expect(contact.categories).toBeDefined();
              expect(contact.urls).toBeDefined();
          });
          it("contacts.spec.10 should be able to define a ContactName object", function() {
              var contactName = new ContactName("Dr. First Last Jr.", "Last", "First", "Middle", "Dr.", "Jr.");
              expect(contactName).toBeDefined();
              expect(contactName.formatted).toBe("Dr. First Last Jr.");
              expect(contactName.familyName).toBe("Last");
              expect(contactName.givenName).toBe("First");
              expect(contactName.middleName).toBe("Middle");
              expect(contactName.honorificPrefix).toBe("Dr.");
              expect(contactName.honorificSuffix).toBe("Jr.");
          });
          it("contacts.spec.11 should be able to define a ContactField object", function() {
              var contactField = new ContactField("home", "8005551212", true);
              expect(contactField).toBeDefined();
              expect(contactField.type).toBe("home");
              expect(contactField.value).toBe("8005551212");
              expect(contactField.pref).toBe(true);
          });
          it("contacts.spec.12 ContactField object should coerce type and value properties to strings", function() {
              var contactField = new ContactField(12345678, 12345678, true);
              expect(contactField.type).toBe("12345678");
              expect(contactField.value).toBe("12345678");
          });
          it("contacts.spec.13 should be able to define a ContactAddress object", function() {
              var contactAddress = new ContactAddress(true, "home", "a","b","c","d","e","f");
              expect(contactAddress).toBeDefined();
              expect(contactAddress.pref).toBe(true);
              expect(contactAddress.type).toBe("home");
              expect(contactAddress.formatted).toBe("a");
              expect(contactAddress.streetAddress).toBe("b");
              expect(contactAddress.locality).toBe("c");
              expect(contactAddress.region).toBe("d");
              expect(contactAddress.postalCode).toBe("e");
              expect(contactAddress.country).toBe("f");
          });
          it("contacts.spec.14 should be able to define a ContactOrganization object", function() {
              var contactOrg = new ContactOrganization(true, "home", "a","b","c","d","e","f","g");
              expect(contactOrg).toBeDefined();
              expect(contactOrg.pref).toBe(true);
              expect(contactOrg.type).toBe("home");
              expect(contactOrg.name).toBe("a");
              expect(contactOrg.department).toBe("b");
              expect(contactOrg.title).toBe("c");
          });
          it("contacts.spec.15 should be able to define a ContactFindOptions object", function() {
              var contactFindOptions = new ContactFindOptions("a", true, "b");
              expect(contactFindOptions).toBeDefined();
              expect(contactFindOptions.filter).toBe("a");
              expect(contactFindOptions.multiple).toBe(true);
          });
          it("contacts.spec.16 should contain a clone function", function() {
              var contact = new Contact();
              expect(contact.clone).toBeDefined();
              expect(typeof contact.clone).toBe('function');
          });
          it("contacts.spec.17 clone function should make deep copy of Contact Object", function() {
              var contact = new Contact();
              contact.id=1;
              contact.displayName="Test Name";
              contact.nickname="Testy";
              contact.gender="male";
              contact.note="note to be cloned";
              contact.name = new ContactName("Mr. Test Name");

              var clonedContact = contact.clone();

              expect(contact.id).toBe(1);
              expect(clonedContact.id).toBe(null);
              expect(clonedContact.displayName).toBe(contact.displayName);
              expect(clonedContact.nickname).toBe(contact.nickname);
              expect(clonedContact.gender).toBe(contact.gender);
              expect(clonedContact.note).toBe(contact.note);
              expect(clonedContact.name.formatted).toBe(contact.name.formatted);
              expect(clonedContact.connected).toBe(contact.connected);
          });
          it("contacts.spec.18 should contain a save function", function() {
              var contact = new Contact();
              expect(contact.save).toBeDefined();
              expect(typeof contact.save).toBe('function');
          });
          it("contacts.spec.19 should contain a remove function", function() {
              var contact = new Contact();
              expect(contact.remove).toBeDefined();
              expect(typeof contact.remove).toBe('function');
          });
      });
      describe('save method', function () {
          it("contacts.spec.20 should be able to save a contact", function(done) {
              var bDay = new Date(1976, 6,4);
              gContactObj = navigator.contacts.create({"gender": "male", "note": "my note", "name": {"familyName": "Delete", "givenName": "Test"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});

              var saveSuccess = function(obj) {
                      expect(obj).toBeDefined();
                      expect(obj.note).toBe('my note');
                      expect(obj.name.familyName).toBe('Delete');
                      expect(obj.name.givenName).toBe('Test');
                      expect(obj.emails.length).toBe(2);
                      expect(obj.emails[0].value).toBe('here@there.com');
                      expect(obj.emails[1].value).toBe('there@here.com');
                      expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
                      expect(obj.addresses).toBe(null);
                      // must store returned object in order to have id for update test below
                      gContactObj = obj;
                      done();
                  },
                  saveFail = fail;

              gContactObj.save(saveSuccess, saveFail);
           });
          // HACK: there is a reliance between the previous and next test. This is bad form.
          it("contacts.spec.21 update a contact", function(done) {
              expect(gContactObj).toBeDefined();

              var bDay = new Date(1975, 5,4);
              var noteText = "an UPDATED note";

              var win = function(obj) {
                      expect(obj).toBeDefined();
                      expect(obj.id).toBe(gContactObj.id);
                      expect(obj.note).toBe(noteText);
                      expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
                      expect(obj.emails.length).toBe(1);
                      expect(obj.emails[0].value).toBe('here@there.com');
                      removeContact();         // Clean up contact object
                      done();
                  }, fail = function() { removeContact(); fail(done); };

              // remove an email
              gContactObj.emails[1].value = "";
              // change birthday
              gContactObj.birthday = bDay;
              // update note
              gContactObj.note = noteText;
              gContactObj.save(win, fail);
          });
      });
      describe('Contact.remove method', function (done) {
          afterEach(removeContact);

          it("contacts.spec.22 calling remove on a contact has an id of null should return ContactError.UNKNOWN_ERROR", function(done) {
              var win = function() {};
              var fail = function(result) {
                  expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
                  done();
              };

              var rmContact = new Contact();
              rmContact.remove(win, fail);
          });
          it("contacts.spec.23 calling remove on a contact that does not exist should return ContactError.UNKNOWN_ERROR", function(done) {
              var rmWin = fail;
              var rmFail = function(result) {
                  expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
                  done();
              };

              var rmContact = new Contact();
              // this is a bit risky as some devices may have contact ids that large
              var contact = new Contact("this string is supposed to be a unique identifier that will never show up on a device");
              contact.remove(rmWin, rmFail);
          });
      });
      describe("Round trip Contact tests (creating + save + delete + find).", function () {
          afterEach(removeContact);

          it("contacts.spec.24 Creating, saving, finding a contact should work, removing it should work, after which we should not be able to find it, and we should not be able to delete it again.", function(done) {
              gContactObj = new Contact();
              gContactObj.name = new ContactName();
              gContactObj.name.familyName = "DeleteMe";
              gContactObj.save(function(c_obj) {
                  var findWin = function(cs) {
                      expect(cs.length).toBe(1);
                      // update to have proper saved id
                      gContactObj = cs[0];
                      gContactObj.remove(function() {
                          var findWinAgain = function(seas) {
                              expect(seas.length).toBe(0);
                              gContactObj.remove(function() {
                                  throw("success callback called after non-existent Contact object called remove(). Test failed.");
                              }, function(e) {
                                  expect(e.code).toBe(ContactError.UNKNOWN_ERROR);
                                  done();
                              });
                          };
                          var findFailAgain = function(e) {
                              throw("find error callback invoked after delete, test failed.");
                          };
                          var obj = new ContactFindOptions();
                          obj.filter="DeleteMe";
                          obj.multiple=true;
                          navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWinAgain, findFailAgain, obj);
                      }, function(e) {
                          throw("Newly created contact's remove function invoked error callback. Test failed.");
                      });
                  };
                  var findFail = fail;
                  var obj = new ContactFindOptions();
                  obj.filter="DeleteMe";
                  obj.multiple=true;
                  navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
              }, fail);
          });
      });
      describe('ContactError interface', function () {
          it("contacts.spec.25 ContactError constants should be defined", function() {
              expect(ContactError.UNKNOWN_ERROR).toBe(0);
              expect(ContactError.INVALID_ARGUMENT_ERROR).toBe(1);
              expect(ContactError.TIMEOUT_ERROR).toBe(2);
              expect(ContactError.PENDING_OPERATION_ERROR).toBe(3);
              expect(ContactError.IO_ERROR).toBe(4);
              expect(ContactError.NOT_SUPPORTED_ERROR).toBe(5);
              expect(ContactError.PERMISSION_DENIED_ERROR).toBe(20);
          });
      });
  });
};
<|endoftext|>


<commit_before>import * as R from 'ramda'

import * as V from '../dist/partial.lenses.validation.cjs'

function show(x) {
  switch (typeof x) {
    case 'string':
    case 'object':
      return JSON.stringify(x)
    default:
      return `${x}`
  }
}

function testEq(expect, thunk) {
  const expr = thunk
    .toString()
    .replace(/\s+/g, ' ')
    .replace(/^\s*function\s*\(\s*\)\s*{\s*(return\s*)?/, '')
    .replace(/\s*;?\s*}\s*$/, '')

  it(`${expr} => ${show(expect)}`, () => {
    const actual = thunk()
    if (!R.equals(actual, expect))
      throw new Error(`Expected: ${show(expect)}, actual: ${show(actual)}`)
  })
}

describe('validation', () => {
  testEq(
    [
      {
        id: 1,
        rules: [null, 'b', 'a'],
        cases: [null, 1, 2]
      }
    ],
    () =>
      V.validate(
        V.arrayId(
          V.cases(
            [
              R.has('id'),
              V.object(['id'], {
                rules: V.arrayIx(V.unless([R.lt(1), 'a'], [R.lt(2), 'b'])),
                cases: V.arrayIx(
                  V.cases([R.has('a'), V.reject(1)], [R.has('b'), V.reject(2)])
                )
              })
            ],
            [
              R.T,
              V.object([], {
                array: V.arrayIx(V.accept)
              })
            ]
          )
        ),
        [
          {
            id: 1,
            rules: [3, 2, 1],
            cases: [{}, { a: 0 }, { b: 0 }]
          },
          { array: [] }
        ]
      )
  )
})

describe('deprecated', () => {
  testEq('yes', () =>
    V.validate(V.unless(R.equals(1), 'no', R.equals(2), 'yes'), 1)
  )
})

if (process.env.NODE_ENV !== 'production') {
  describe('diagnostics', () => {
    it('V.cases throws if not given an even number of arguments', () => {
      try {
        V.cases(R.T)
      } catch (_) {
        return
      }
      throw Error('unexpected')
    })

    it('V.cases throws if not given pairs as arguments', () => {
      try {
        V.cases([R.T])
      } catch (_) {
        return
      }
      throw Error('unexpected')
    })
  })
}
<commit_msg>Test that empty `cases()` and `unless()` are equivalent to `accept`<commit_after>import * as R from 'ramda'

import * as V from '../dist/partial.lenses.validation.cjs'

function show(x) {
  switch (typeof x) {
    case 'string':
    case 'object':
      return JSON.stringify(x)
    default:
      return `${x}`
  }
}

function testEq(expect, thunk) {
  const expr = thunk
    .toString()
    .replace(/\s+/g, ' ')
    .replace(/^\s*function\s*\(\s*\)\s*{\s*(return\s*)?/, '')
    .replace(/\s*;?\s*}\s*$/, '')

  it(`${expr} => ${show(expect)}`, () => {
    const actual = thunk()
    if (!R.equals(actual, expect))
      throw new Error(`Expected: ${show(expect)}, actual: ${show(actual)}`)
  })
}

describe('validation', () => {
  testEq(
    [
      {
        id: 1,
        rules: [null, 'b', 'a'],
        cases: [null, 1, 2]
      }
    ],
    () =>
      V.validate(
        V.arrayId(
          V.cases(
            [
              R.has('id'),
              V.object(['id'], {
                rules: V.arrayIx(V.unless([R.lt(1), 'a'], [R.lt(2), 'b'])),
                cases: V.arrayIx(
                  V.cases([R.has('a'), V.reject(1)], [R.has('b'), V.reject(2)])
                )
              })
            ],
            [
              R.T,
              V.object([], {
                array: V.arrayIx(V.accept)
              })
            ]
          )
        ),
        [
          {
            id: 1,
            rules: [3, 2, 1],
            cases: [{}, { a: 0 }, { b: 0 }]
          },
          { array: [] }
        ]
      )
  )

  testEq(undefined, () => V.validate(V.cases(), 'anything'))
  testEq(undefined, () => V.validate(V.unless(), 'anything'))
})

describe('deprecated', () => {
  testEq('yes', () =>
    V.validate(V.unless(R.equals(1), 'no', R.equals(2), 'yes'), 1)
  )
})

if (process.env.NODE_ENV !== 'production') {
  describe('diagnostics', () => {
    it('V.cases throws if not given an even number of arguments', () => {
      try {
        V.cases(R.T)
      } catch (_) {
        return
      }
      throw Error('unexpected')
    })

    it('V.cases throws if not given pairs as arguments', () => {
      try {
        V.cases([R.T])
      } catch (_) {
        return
      }
      throw Error('unexpected')
    })
  })
}
<|endoftext|>


<commit_before>const fs = require('fs');
const packageJsonPath = __dirname + '/../package.json';
const nightlyPackageName = 'zrender-nightly';

function updateVersion(version) {
    const isNext = process.argv.includes('--next');
    const parts = /(\d+)\.(\d+)\.(\d+)($|\-)/.exec(version);
    if (!parts) {
        throw new Error(`Invalid version number ${version}`);
    }
    // Add date to version.
    const major = +parts[1];
    let minor = +parts[2];
    let patch = +parts[3];
    const isStable = !parts[4];
    if (isStable) {
        // It's previous stable version. Dev version should be higher.
        if (isNext) {
            // Increase minor version for next branch.
            minor++;
        }
        else {
            // Increase main version for master branch.
            patch++;
        }
    }

    const date = new Date().toISOString().replace(/:|T|\.|-/g, '').slice(0, 8);
    return `${major}.${minor}.${patch}-dev.${date}`;
}

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
packageJson.name = nightlyPackageName;
packageJson.version = updateVersion(packageJson.version);

fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');<commit_msg>chore: fix patch version of next build<commit_after>const fs = require('fs');
const packageJsonPath = __dirname + '/../package.json';
const nightlyPackageName = 'zrender-nightly';

function updateVersion(version) {
    const isNext = process.argv.includes('--next');
    const parts = /(\d+)\.(\d+)\.(\d+)($|\-)/.exec(version);
    if (!parts) {
        throw new Error(`Invalid version number ${version}`);
    }
    // Add date to version.
    const major = +parts[1];
    let minor = +parts[2];
    let patch = +parts[3];
    const isStable = !parts[4];
    if (isStable) {
        // It's previous stable version. Dev version should be higher.
        if (isNext) {
            // Increase minor version for next branch.
            minor++;
            patch = 0;
        }
        else {
            // Increase main version for master branch.
            patch++;
        }
    }

    const date = new Date().toISOString().replace(/:|T|\.|-/g, '').slice(0, 8);
    return `${major}.${minor}.${patch}-dev.${date}`;
}

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
packageJson.name = nightlyPackageName;
packageJson.version = updateVersion(packageJson.version);

fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');<|endoftext|>


<commit_before>var Timer = require('../lib/timer');
var expect = require('expect.js');

suite('timer.js', function(){
  var timer;

  suite('#constructor', function(){
    test('proper initialization', function(){
      expect(function(){
        timer = new Timer();
      }).not.to.throwException();
    });

    test('expected attributes', function(){
      expect(timer.currentDuration).to.be(0);
      expect(timer.initialDuration).to.be(0);
    });
  });

  suite('#on', function(){
    test('basics', function(){
      expect(timer.events).to.be.an('object');
      expect(timer.events).to.be.empty();
    });

    test('assigning functions', function(){
      expect(function(){
        timer.on('stuff', function doStuff(){
          return 'stuffDone';
        });
      }).not.to.throwException(TypeError);

      timer.on('stuff', function doStuff2(){});
      timer.on('interval', function doInterval(){});

      expect(timer.events.stuff.length).to.be(2);
      expect(timer.events.stuff[0]()).to.be('stuffDone');
      expect(timer.events.interval.length).to.be(1);
    });
  });

  suite('#emits', function(){

  });

  suite('#on', function(){

  });

  suite('#start', function(){

  });

  suite('#pause', function(){

  });

  suite('#reset', function(){

  });

  suite('#stop', function(){

  });

  suite('#toggle', function(){

  });

  suite('#setDuration', function(){

  });

  suite('#onInterval', function(){

  });

  suite('#sanitizeDuration', function(){

  });
});<commit_msg>`.emit` method<commit_after>var Timer = require('../lib/timer');
var expect = require('expect.js');

suite('timer.js', function(){
  var timer;

  suite('#constructor', function(){
    test('proper initialization', function(){
      expect(function(){
        timer = new Timer();
      }).not.to.throwException();
    });

    test('expected attributes', function(){
      expect(timer.currentDuration).to.be(0);
      expect(timer.initialDuration).to.be(0);
    });
  });

  suite('#on', function(){
    test('basics', function(){
      expect(timer.events).to.be.an('object');
      expect(timer.events).to.be.empty();
    });

    test('assigning functions', function(){
      expect(function(){
        timer.on('stuff', function doStuff(){
          return 'stuffDone';
        });
      }).not.to.throwException(TypeError);

      timer.on('stuff', function doStuff2(){});
      timer.on('interval', function doInterval(){});

      expect(timer.events.stuff.length).to.be(2);
      expect(timer.events.stuff[0]()).to.be('stuffDone');
      expect(timer.events.interval.length).to.be(1);
    });
  });

  suite('#emits', function(){
    test('existing event', function(done){
      timer = new Timer();

      timer.on('interval', function(){
        expect(timer.events.interval.length).to.be(1);
        done();
      });

      timer.emit('interval');
    });

    test('unexisting event', function(){
      expect(function(){
        timer.emit('foobar');
      }).not.to.throwException();
    });
  });

  suite('#start', function(){

  });

  suite('#pause', function(){

  });

  suite('#reset', function(){

  });

  suite('#stop', function(){

  });

  suite('#toggle', function(){

  });

  suite('#setDuration', function(){

  });

  suite('#onInterval', function(){

  });

  suite('#sanitizeDuration', function(){

  });
});<|endoftext|>


<commit_before>require('babel-register')

const webpack = require('webpack')
const cssnano = require('cssnano')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const config = require('../config/')
const _debug = require('debug')
const postcssImport = require('postcss-import')
const precss = require('precss')
const paths = config.utils_paths

const debug = _debug('app:webpack:config')
const {__DEV__, __PROD__, __TEST__} = config.globals

debug('Create configuration.')
const webpackConfig = {
  name: 'client',
  target: 'web',
  devtool: 'cheap-source-map',
  resolve: {
    root: paths.client(),
    extensions: ['', '.js', '.jsx', '.json']
  },
  module: {},
  modulesDirectories: ['src', 'node_modules'],
  colors: true,
  stats: {
    colors: true,
    reasons: true
  }
}

const APP_ENTRY_PATHS = [
  'babel-polyfill',
  paths.client('main.jsx')
]

webpackConfig.entry = {
  app: __DEV__
    ? APP_ENTRY_PATHS.concat(`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`)
    : APP_ENTRY_PATHS,
  vendor: config.vendor_packages
}

webpackConfig.output = {
  filename: '[name].[hash].js',
  path: paths.base(config.dir_dist),
  publicPath: config.compiler_public_path
}

webpackConfig.plugins = [
  new webpack.DefinePlugin(config.globals),
  new HtmlWebpackPlugin({
    template: paths.client('index.html'),
    hash: false,
    // Uncomment if you want a favicon
    // favicon: path.resolve(__dirname, '../src/static') + '/favicon.ico',
    filename: 'index.html',
    inject: 'body',
    minify: {
      collapseWhitespace: true
    }
  })
]

if (__DEV__) {
  debug('Enable plugins for live development (HMR, NoErrors).')
  webpackConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  )
}
else if (__PROD__) {
  debug('Apply UglifyJS plugin.')
  webpackConfig.plugins.push(
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        unused: true,
        dead_code: true,
        drop_console: true,
        screw_ie8: true,
        drop_debugger: true,
        hoist_funs: true,
        evaluate: true,
        comparisons: true,
        properties: true,
        warnings: false
      }
    }),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    })
  )
}

if (!__TEST__) {
  webpackConfig.plugins.push(new webpack.optimize.CommonsChunkPlugin({
    names: ['vendor']
  }))
}

if (!__PROD__) {
  webpackConfig.module.preLoaders = [{
    test: /\.(js|jsx)$/,
    loader: 'eslint',
    exclude: /node_modules/
  }]

  webpackConfig.eslint = {
    configFile: './.eslintrc',
    emitWarning: __DEV__
  }
}

webpackConfig.module.loaders = [{
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  loader: 'babel',
  query: {
    cacheDirectory: true,
    plugins: ['transform-runtime'],
    presets: __DEV__
      ? ['es2015', 'react', 'stage-0']
      : ['es2015', 'react', 'stage-0', 'react-optimize'],
    env: {
      development: {
        plugins: [
          ['react-transform', {
            'transforms': [{
              'transform': 'react-transform-hmr',
              'imports': ['react'],
              'locals': ['module']
            }, {
              'transform': 'react-transform-catch-errors',
              'imports': ['react', 'redbox-react']
            }]
          }],
          'transform-runtime'
        ]
      }
    }
  }
},
{
  test: /\.json$/,
  loader: 'json'
}]

// Styles
const cssLoader = !config.compiler_css_modules
  ? 'css?sourceMap'
  : [
    'css?modules',
    'sourceMap',
    'importLoaders=1',
    'localIdentName=[name]__[local]___[hash:base64:5]'
  ].join('&')

webpackConfig.module.loaders.push({
  test: /\.pcss/,
  include: /src/,
  loaders: [
    'style',
    cssLoader,
    'postcss'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.pcss/,
  exclude: /src/,
  loaders: [
    'style',
    'css?sourceMap',
    'postcss'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.*.global.css/,
  loaders: [
    'style',
    'css?sourceMap',
    'postcss'
  ]
})

webpackConfig.postcss = [
  postcssImport({
    root: process.cwd(),
    path: `${process.cwd()}/node_modules`
  }),
  precss(),
  cssnano({
    sourcemap: !__PROD__,
    compress: __PROD__,
    autoprefixer: {
      add: true,
      remove: true,
      browsers: ['last 2 versions']
    },
    safe: true,
    discardComments: {
      removeAll: true
    }
  })
]

// File loaders
webpackConfig.module.loaders.push({
  test: /\.(jpe?g|png|gif|svg)$/i,
  loaders: [
    'file?hash=sha512&digest=hex&name=[hash].[ext]',
    'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
  ]
})

if (!__DEV__) {
  debug('Apply ExtractTextPlugin to CSS loaders.')
  webpackConfig.module.loaders.filter(loader =>
    loader.loaders && loader.loaders.find(name => /css/.test(name.split('?')[0]))
  ).forEach(loader => {
    const [first, ...rest] = loader.loaders
    loader.loader = ExtractTextPlugin.extract(first, rest.join('!'))
    delete loader.loaders
  })

  webpackConfig.plugins.push(
    new ExtractTextPlugin('[name].[contenthash].css', {
      allChunks: true
    })
  )
}

module.exports = webpackConfig
<commit_msg>Fix css-modules<commit_after>require('babel-register')

const webpack = require('webpack')
const cssnano = require('cssnano')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const config = require('../config/')
const _debug = require('debug')
const postcssImport = require('postcss-import')
const precss = require('precss')
const paths = config.utils_paths

const debug = _debug('app:webpack:config')
const {__DEV__, __PROD__, __TEST__} = config.globals

debug('Create configuration.')
const webpackConfig = {
  name: 'client',
  target: 'web',
  devtool: 'cheap-source-map',
  resolve: {
    root: paths.client(),
    extensions: ['', '.js', '.jsx', '.json']
  },
  module: {},
  modulesDirectories: ['src', 'node_modules'],
  colors: true,
  stats: {
    colors: true,
    reasons: true
  }
}

const APP_ENTRY_PATHS = [
  'babel-polyfill',
  paths.client('main.jsx')
]

webpackConfig.entry = {
  app: __DEV__
    ? APP_ENTRY_PATHS.concat(`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`)
    : APP_ENTRY_PATHS,
  vendor: config.vendor_packages
}

webpackConfig.output = {
  filename: '[name].[hash].js',
  path: paths.base(config.dir_dist),
  publicPath: config.compiler_public_path
}

webpackConfig.plugins = [
  new webpack.DefinePlugin(config.globals),
  new HtmlWebpackPlugin({
    template: paths.client('index.html'),
    hash: false,
    // Uncomment if you want a favicon
    // favicon: path.resolve(__dirname, '../src/static') + '/favicon.ico',
    filename: 'index.html',
    inject: 'body',
    minify: {
      collapseWhitespace: true
    }
  })
]

if (__DEV__) {
  debug('Enable plugins for live development (HMR, NoErrors).')
  webpackConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  )
}
else if (__PROD__) {
  debug('Apply UglifyJS plugin.')
  webpackConfig.plugins.push(
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        unused: true,
        dead_code: true,
        drop_console: true,
        screw_ie8: true,
        drop_debugger: true,
        hoist_funs: true,
        evaluate: true,
        comparisons: true,
        properties: true,
        warnings: false
      }
    }),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    })
  )
}

if (!__TEST__) {
  webpackConfig.plugins.push(new webpack.optimize.CommonsChunkPlugin({
    names: ['vendor']
  }))
}

if (!__PROD__) {
  webpackConfig.module.preLoaders = [{
    test: /\.(js|jsx)$/,
    loader: 'eslint',
    exclude: /node_modules/
  }]

  webpackConfig.eslint = {
    configFile: './.eslintrc',
    emitWarning: __DEV__
  }
}

webpackConfig.module.loaders = [{
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  loader: 'babel',
  query: {
    cacheDirectory: true,
    plugins: ['transform-runtime'],
    presets: __DEV__
      ? ['es2015', 'react', 'stage-0']
      : ['es2015', 'react', 'stage-0', 'react-optimize'],
    env: {
      development: {
        plugins: [
          ['react-transform', {
            'transforms': [{
              'transform': 'react-transform-hmr',
              'imports': ['react'],
              'locals': ['module']
            }, {
              'transform': 'react-transform-catch-errors',
              'imports': ['react', 'redbox-react']
            }]
          }],
          'transform-runtime'
        ]
      }
    }
  }
},
{
  test: /\.json$/,
  loader: 'json'
}]

// Styles
const cssLoader = [
  'css?modules',
  'sourceMap',
  'importLoaders=1',
  'localIdentName=[name]__[local]___[hash:base64:5]'
].join('&')

webpackConfig.module.loaders.push({
  test: /\.pcss/,
  include: /src/,
  loaders: [
    'style',
    cssLoader,
    'postcss'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.pcss/,
  exclude: /src/,
  loaders: [
    'style',
    'css?sourceMap',
    'postcss'
  ]
})

webpackConfig.module.loaders.push({
  test: /\.*.global.css/,
  loaders: [
    'style',
    'css?sourceMap',
    'postcss'
  ]
})

webpackConfig.postcss = [
  postcssImport({
    root: process.cwd(),
    path: `${process.cwd()}/node_modules`
  }),
  precss(),
  cssnano({
    sourcemap: !__PROD__,
    compress: __PROD__,
    autoprefixer: {
      add: true,
      remove: true,
      browsers: ['last 2 versions']
    },
    safe: true,
    discardComments: {
      removeAll: true
    }
  })
]

// File loaders
webpackConfig.module.loaders.push({
  test: /\.(jpe?g|png|gif|svg)$/i,
  loaders: [
    'file?hash=sha512&digest=hex&name=[hash].[ext]',
    'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
  ]
})

if (!__DEV__) {
  debug('Apply ExtractTextPlugin to CSS loaders.')
  webpackConfig.module.loaders.filter(loader =>
    loader.loaders && loader.loaders.find(name => /css/.test(name.split('?')[0]))
  ).forEach(loader => {
    const [first, ...rest] = loader.loaders
    loader.loader = ExtractTextPlugin.extract(first, rest.join('!'))
    delete loader.loaders
  })

  webpackConfig.plugins.push(
    new ExtractTextPlugin('[name].[contenthash].css', {
      allChunks: true
    })
  )
}

module.exports = webpackConfig
<|endoftext|>


<commit_before>const webpack = require("webpack");
const cssnano = require("cssnano");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const config = require("../config");
const debug = require("debug")("app:webpack:config");
var path = require("path");

const paths = config.utils_paths;
const {__DEV__, __PROD__, __TEST__} = config.globals;
const {QUICK_DEPLOY, USE_TSLOADER} = process.env;

debug("Creating configuration.");
const webpackConfig = {
	name: "client",
	target: "web",
	devtool: config.compiler_devtool,
	resolve: {
		root: paths.client(),
		extensions: ["", ".js", ".jsx", ".json"].concat(USE_TSLOADER ? [".ts", ".tsx"] : []),
		fallback: [path.join(__dirname, "node_modules")],
		alias: {
            //"react": __dirname + "/node_modules/react/",
            "react": paths.base() + "/node_modules/react/",
			"react-dom": paths.base() + "/node_modules/react-dom/",
			//"@types/react": paths.base() + "/node_modules/@types/react/",
        }
	},
	// same issue, for loaders like babel
	resolveLoader: {
		fallback: [path.join(__dirname, "node_modules")]
	},
	module: {}
};

if (__PROD__) {
	webpackConfig.module.preLoaders = [
		{test: /\.jsx?$/, loader: "source-map", exclude: /react-hot-loader/}
	];
}

// Entry Points
// ==========

const APP_ENTRY = paths.client(USE_TSLOADER ? "Main.ts" : "Main.js");

webpackConfig.entry = {
	app: __DEV__
		? [APP_ENTRY].concat(`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`)
		: [APP_ENTRY],
	//vendor: config.compiler_vendors
};

// Bundle Output
// ==========

webpackConfig.output = {
	filename: `[name].[${config.compiler_hash_type}].js`,
	path: paths.dist(),
	//path: path.resolve(__dirname, "dist"),
	publicPath: config.compiler_public_path,
	pathinfo: true, // include comments next to require-funcs saying path // (this seems to break webpack-runtime-require)
}

// Plugins
// ==========

//let ExposeRequirePlugin = require("webpack-expose-require-plugin");\
var HappyPack = require('happypack');

webpackConfig.plugins = [
	// Plugin to show any webpack warnings and prevent tests from running
	function() {
		let errors = []
		this.plugin("done", function (stats) {
			if (stats.compilation.errors.length) {
				// Log each of the warnings
				stats.compilation.errors.forEach(function (error) {
					errors.push(error.message || error)
				})

				// Pretend no assets were generated. This prevents the tests from running, making it clear that there were warnings.
				//throw new Error(errors)
			}
		})
	},
	new webpack.DefinePlugin(config.globals),
	new HtmlWebpackPlugin({
		//template: paths.client("index.html"),
		template: paths.base("Source/index.html"),
		hash: false,
		// favicon: paths.client("Resources/favicon.ico"), // for including single favicon
		filename: "index.html",
		inject: "body",
		minify: {
			collapseWhitespace: true
		}
	}),
	/*new ExposeRequirePlugin({
		level: "dependency", // "all", "dependency", "application"
		pathPrefix: "Source", // in case if your source is not placed in root folder.
	}),*/

	new HappyPack({
		// loaders is the only required parameter:
		//loaders: [ 'babel?presets[]=es2015' ],
		loaders: ["babel"],
	}),

	new webpack.DllReferencePlugin({
		context: path.join(__dirname, "Source"),
		//context: paths.base(),
		manifest: require("../config/dll/vendor-manifest.json")
	}),
]

if (__DEV__) {
	debug("Enable plugins for live development (HMR, NoErrors).")
	webpackConfig.plugins.push(
		new webpack.HotModuleReplacementPlugin(),
		new webpack.NoErrorsPlugin()
	);
} else if (__PROD__ && !QUICK_DEPLOY) {
	debug("Enable plugins for production (OccurenceOrder, Dedupe & UglifyJS).")
	webpackConfig.plugins.push(
		new webpack.optimize.OccurrenceOrderPlugin(),
		new webpack.optimize.DedupePlugin(),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
				unused: true,
				dead_code: true,
				warnings: false,
				keep_fnames: true,
			},
			mangle: {
				keep_fnames: true,
			}
		})
	)
}

// Don't split bundles during testing, since we only want import one bundle
if (!__TEST__) {
	webpackConfig.plugins.push(
		// maybe temp; the only reason we keep this for now, is because it makes the webpackJsonp function available (used in webpack-runtime-require)
		new webpack.optimize.CommonsChunkPlugin({
			names: ["vendor"]
		})
	)
}

// Loaders
// ==========

// JavaScript / JSON
webpackConfig.module.loaders = [
	{
		test: USE_TSLOADER ? /\.(jsx?|tsx?)$/ : /\.jsx?$/,
		//exclude: [/node_modules/, /react-redux-firebase/],
		include: [paths.client()],
		//loader: "babel",
		//loaders: ["happypack/loader"],
		loader: "happypack/loader",
		query: config.compiler_babel
	},
	{
		test: /\.json$/,
		loader: "json"
	},
];
if (USE_TSLOADER) {
	//webpackConfig.module.loaders.push({test: /\.tsx?$/, loader: "awesome-typescript-loader"});
	webpackConfig.module.loaders.push({test: /\.tsx?$/, loader: "ts-loader", query: {include: [paths.client()]}});
}

// Style Loaders
// ==========

// We use cssnano with the postcss loader, so we tell
// css-loader not to duplicate minimization.
const BASE_CSS_LOADER = "css?sourceMap&-minimize"

// Add any packge names here whose styles need to be treated as CSS modules.
// These paths will be combined into a single regex.
const PATHS_TO_TREAT_AS_CSS_MODULES = [
	// "react-toolbox", (example)
]

// If config has CSS modules enabled, treat this project"s styles as CSS modules.
if (config.compiler_css_modules) {
	PATHS_TO_TREAT_AS_CSS_MODULES.push(
		paths.client().replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, "\\$&") // eslint-disable-line
	)
}
const isUsingCSSModules = !!PATHS_TO_TREAT_AS_CSS_MODULES.length
const cssModulesRegex = new RegExp(`(${PATHS_TO_TREAT_AS_CSS_MODULES.join("|")})`)

// Loaders for styles that need to be treated as CSS modules.
if (isUsingCSSModules) {
	const cssModulesLoader = [
		BASE_CSS_LOADER,
		"modules",
		"importLoaders=1",
		"localIdentName=[name]__[local]___[hash:base64:5]"
	].join("&")

	webpackConfig.module.loaders.push({
		test: /\.scss$/,
		include: cssModulesRegex,
		loaders: [
			"style",
			cssModulesLoader,
			"postcss",
			"sass?sourceMap"
		]
	})

	webpackConfig.module.loaders.push({
		test: /\.css$/,
		include: cssModulesRegex,
		loaders: [
			"style",
			cssModulesLoader,
			"postcss"
		]
	})
}

// Loaders for files that should not be treated as CSS modules.
const excludeCSSModules = isUsingCSSModules ? cssModulesRegex : false
webpackConfig.module.loaders.push({
	test: /\.scss$/,
	exclude: excludeCSSModules,
	loaders: [
		"style",
		BASE_CSS_LOADER,
		"postcss",
		"sass?sourceMap"
	]
})
webpackConfig.module.loaders.push({
	test: /\.css$/,
	exclude: excludeCSSModules,
	loaders: [
		"style",
		BASE_CSS_LOADER,
		"postcss"
	]
})

webpackConfig.sassLoader = {
	includePaths: paths.client("styles")
}

webpackConfig.postcss = [
	cssnano({
		autoprefixer: {
			add: true,
			remove: true,
			browsers: ["last 2 versions"]
		},
		discardComments: {
			removeAll: true
		},
		discardUnused: false,
		mergeIdents: false,
		reduceIdents: false,
		safe: true,
		sourcemap: true
	})
]

// File loaders
/* eslint-disable */
webpackConfig.module.loaders.push(
	{ test: /\.woff(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff" },
	{ test: /\.woff2(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2" },
	{ test: /\.otf(\?.*)?$/, loader: "file?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype" },
	{ test: /\.ttf(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream" },
	{ test: /\.eot(\?.*)?$/, loader: "file?prefix=fonts/&name=[path][name].[ext]" },
	{ test: /\.svg(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml" },
	{ test: /\.(png|jpg)$/, loader: "url?limit=8192" }
)
/* eslint-enable */

// Finalize Configuration
// ==========

// when we don"t know the public path (we know it only when HMR is enabled [in development]) we
// need to use the extractTextPlugin to fix this issue:
// http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809
if (!__DEV__ && !__TEST__) {
	debug("Apply ExtractTextPlugin to CSS loaders.")
	webpackConfig.module.loaders.filter((loader) =>
		loader.loaders && loader.loaders.find((name) => /css/.test(name.split("?")[0]))
	).forEach((loader) => {
		const first = loader.loaders[0]
		const rest = loader.loaders.slice(1)
		loader.loader = ExtractTextPlugin.extract(first, rest.join("!"))
		delete loader.loaders
	})

	webpackConfig.plugins.push(
		new ExtractTextPlugin("[name].[contenthash].css", {
			allChunks: true
		})
	)
}

module.exports = webpackConfig;<commit_msg>* Hashes for js/css files are now added to the search/query part of the url. Thus, we get cache-busting + google's cached html can still load! (it just loads whatever the current js content is -- which is better than loading nothing!)<commit_after>const webpack = require("webpack");
const cssnano = require("cssnano");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const config = require("../config");
const debug = require("debug")("app:webpack:config");
var path = require("path");

const paths = config.utils_paths;
const {__DEV__, __PROD__, __TEST__} = config.globals;
const {QUICK_DEPLOY, USE_TSLOADER} = process.env;

debug("Creating configuration.");
const webpackConfig = {
	name: "client",
	target: "web",
	devtool: config.compiler_devtool,
	resolve: {
		root: paths.client(),
		extensions: ["", ".js", ".jsx", ".json"].concat(USE_TSLOADER ? [".ts", ".tsx"] : []),
		fallback: [path.join(__dirname, "node_modules")],
		alias: {
			//"react": __dirname + "/node_modules/react/",
			"react": paths.base() + "/node_modules/react/",
			"react-dom": paths.base() + "/node_modules/react-dom/",
			//"@types/react": paths.base() + "/node_modules/@types/react/",
		}
	},
	// same issue, for loaders like babel
	resolveLoader: {
		fallback: [path.join(__dirname, "node_modules")]
	},
	module: {}
};

if (__PROD__) {
	webpackConfig.module.preLoaders = [
		{test: /\.jsx?$/, loader: "source-map", exclude: /react-hot-loader/}
	];
}

// Entry Points
// ==========

const APP_ENTRY = paths.client(USE_TSLOADER ? "Main.ts" : "Main.js");

webpackConfig.entry = {
	app: __DEV__
		? [APP_ENTRY].concat(`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`)
		: [APP_ENTRY],
	//vendor: config.compiler_vendors
};

// Bundle Output
// ==========

webpackConfig.output = {
	//filename: `[name].[${config.compiler_hash_type}].js`,
	filename: `[name].js?[${config.compiler_hash_type}]`, // have js/css files have static names, so google can still display content (even when js file has changed)
	path: paths.dist(),
	//path: path.resolve(__dirname, "dist"),
	publicPath: config.compiler_public_path,
	pathinfo: true, // include comments next to require-funcs saying path // (this seems to break webpack-runtime-require)
}

// Plugins
// ==========

//let ExposeRequirePlugin = require("webpack-expose-require-plugin");\
var HappyPack = require('happypack');

webpackConfig.plugins = [
	// Plugin to show any webpack warnings and prevent tests from running
	function() {
		let errors = []
		this.plugin("done", function (stats) {
			if (stats.compilation.errors.length) {
				// Log each of the warnings
				stats.compilation.errors.forEach(function (error) {
					errors.push(error.message || error)
				})

				// Pretend no assets were generated. This prevents the tests from running, making it clear that there were warnings.
				//throw new Error(errors)
			}
		})
	},
	new webpack.DefinePlugin(config.globals),
	new HtmlWebpackPlugin({
		//template: paths.client("index.html"),
		template: paths.base("Source/index.html"),
		hash: false,
		// favicon: paths.client("Resources/favicon.ico"), // for including single favicon
		filename: "index.html",
		inject: "body",
		minify: {
			collapseWhitespace: true
		}
	}),
	/*new ExposeRequirePlugin({
		level: "dependency", // "all", "dependency", "application"
		pathPrefix: "Source", // in case if your source is not placed in root folder.
	}),*/

	new HappyPack({
		// loaders is the only required parameter:
		//loaders: [ 'babel?presets[]=es2015' ],
		loaders: ["babel"],
	}),

	new webpack.DllReferencePlugin({
		context: path.join(__dirname, "Source"),
		//context: paths.base(),
		manifest: require("../config/dll/vendor-manifest.json")
	}),
]

if (__DEV__) {
	debug("Enable plugins for live development (HMR, NoErrors).")
	webpackConfig.plugins.push(
		new webpack.HotModuleReplacementPlugin(),
		new webpack.NoErrorsPlugin()
	);
} else if (__PROD__ && !QUICK_DEPLOY) {
	debug("Enable plugins for production (OccurenceOrder, Dedupe & UglifyJS).")
	webpackConfig.plugins.push(
		new webpack.optimize.OccurrenceOrderPlugin(),
		new webpack.optimize.DedupePlugin(),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
				unused: true,
				dead_code: true,
				warnings: false,
				keep_fnames: true,
			},
			mangle: {
				keep_fnames: true,
			}
		})
	)
}

// Don't split bundles during testing, since we only want import one bundle
if (!__TEST__) {
	webpackConfig.plugins.push(
		// maybe temp; the only reason we keep this for now, is because it makes the webpackJsonp function available (used in webpack-runtime-require)
		new webpack.optimize.CommonsChunkPlugin({
			names: ["vendor"]
		})
	)
}

// Loaders
// ==========

// JavaScript / JSON
webpackConfig.module.loaders = [
	{
		test: USE_TSLOADER ? /\.(jsx?|tsx?)$/ : /\.jsx?$/,
		//exclude: [/node_modules/, /react-redux-firebase/],
		include: [paths.client()],
		//loader: "babel",
		//loaders: ["happypack/loader"],
		loader: "happypack/loader",
		query: config.compiler_babel
	},
	{
		test: /\.json$/,
		loader: "json"
	},
];
if (USE_TSLOADER) {
	//webpackConfig.module.loaders.push({test: /\.tsx?$/, loader: "awesome-typescript-loader"});
	webpackConfig.module.loaders.push({test: /\.tsx?$/, loader: "ts-loader", query: {include: [paths.client()]}});
}

// Style Loaders
// ==========

// We use cssnano with the postcss loader, so we tell
// css-loader not to duplicate minimization.
const BASE_CSS_LOADER = "css?sourceMap&-minimize"

// Add any packge names here whose styles need to be treated as CSS modules.
// These paths will be combined into a single regex.
const PATHS_TO_TREAT_AS_CSS_MODULES = [
	// "react-toolbox", (example)
]

// If config has CSS modules enabled, treat this project"s styles as CSS modules.
if (config.compiler_css_modules) {
	PATHS_TO_TREAT_AS_CSS_MODULES.push(
		paths.client().replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, "\\$&") // eslint-disable-line
	)
}
const isUsingCSSModules = !!PATHS_TO_TREAT_AS_CSS_MODULES.length
const cssModulesRegex = new RegExp(`(${PATHS_TO_TREAT_AS_CSS_MODULES.join("|")})`)

// Loaders for styles that need to be treated as CSS modules.
if (isUsingCSSModules) {
	const cssModulesLoader = [
		BASE_CSS_LOADER,
		"modules",
		"importLoaders=1",
		"localIdentName=[name]__[local]___[hash:base64:5]"
	].join("&")

	webpackConfig.module.loaders.push({
		test: /\.scss$/,
		include: cssModulesRegex,
		loaders: [
			"style",
			cssModulesLoader,
			"postcss",
			"sass?sourceMap"
		]
	})

	webpackConfig.module.loaders.push({
		test: /\.css$/,
		include: cssModulesRegex,
		loaders: [
			"style",
			cssModulesLoader,
			"postcss"
		]
	})
}

// Loaders for files that should not be treated as CSS modules.
const excludeCSSModules = isUsingCSSModules ? cssModulesRegex : false
webpackConfig.module.loaders.push({
	test: /\.scss$/,
	exclude: excludeCSSModules,
	loaders: [
		"style",
		BASE_CSS_LOADER,
		"postcss",
		"sass?sourceMap"
	]
})
webpackConfig.module.loaders.push({
	test: /\.css$/,
	exclude: excludeCSSModules,
	loaders: [
		"style",
		BASE_CSS_LOADER,
		"postcss"
	]
})

webpackConfig.sassLoader = {
	includePaths: paths.client("styles")
}

webpackConfig.postcss = [
	cssnano({
		autoprefixer: {
			add: true,
			remove: true,
			browsers: ["last 2 versions"]
		},
		discardComments: {
			removeAll: true
		},
		discardUnused: false,
		mergeIdents: false,
		reduceIdents: false,
		safe: true,
		sourcemap: true
	})
]

// File loaders
/* eslint-disable */
webpackConfig.module.loaders.push(
	{ test: /\.woff(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff" },
	{ test: /\.woff2(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2" },
	{ test: /\.otf(\?.*)?$/, loader: "file?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype" },
	{ test: /\.ttf(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream" },
	{ test: /\.eot(\?.*)?$/, loader: "file?prefix=fonts/&name=[path][name].[ext]" },
	{ test: /\.svg(\?.*)?$/, loader: "url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml" },
	{ test: /\.(png|jpg)$/, loader: "url?limit=8192" }
)
/* eslint-enable */

// Finalize Configuration
// ==========

// when we don"t know the public path (we know it only when HMR is enabled [in development]) we
// need to use the extractTextPlugin to fix this issue:
// http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809
if (!__DEV__ && !__TEST__) {
	debug("Apply ExtractTextPlugin to CSS loaders.")
	webpackConfig.module.loaders.filter((loader) =>
		loader.loaders && loader.loaders.find((name) => /css/.test(name.split("?")[0]))
	).forEach((loader) => {
		const first = loader.loaders[0]
		const rest = loader.loaders.slice(1)
		loader.loader = ExtractTextPlugin.extract(first, rest.join("!"))
		delete loader.loaders
	})

	webpackConfig.plugins.push(
		//new ExtractTextPlugin("[name].[contenthash].css", {allChunks: true})
		new ExtractTextPlugin("[name].css?[contenthash]", {allChunks: true})
	)
}

module.exports = webpackConfig;<|endoftext|>


<commit_before>const webpack = require('webpack')
const { resolve, join } = require('path')
const projectRoot = resolve(__dirname, '../')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const PreloadWebpackPlugin = require('preload-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const StyleLintPlugin = require('stylelint-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const TerserPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

const dev = process.env.NODE_ENV !== 'production'
const cordova = process.env.CORDOVA === 'true'
const { backend, proxyTable } = require('./config')

const appEnv = {
  // define the karrot environment
  __ENV: {
    DEV: dev,
    CORDOVA: cordova,
    BACKEND: JSON.stringify(backend),
    KARROT_THEME: JSON.stringify(process.env.KARROT_THEME),
    FCM_CONFIG: JSON.stringify(process.env.FCM_CONFIG),
    SENTRY_CONFIG: JSON.stringify(process.env.SENTRY_CONFIG),
    GIT_SHA1: JSON.stringify(process.env.GIT_SHA1 || process.env.CIRCLE_SHA1),
  },
  // vuelidate wants this
  // see https://github.com/monterail/vuelidate/issues/365
  'process.env.BUILD': JSON.stringify('web'),
}

const styleLoaders = [
  dev ? 'style-loader' : MiniCssExtractPlugin.loader,
  {
    loader: 'css-loader',
    options: { importLoaders: 1 },
  },
  {
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: loader => [
        require('autoprefixer')(),
      ],
    },
  },
]

module.exports = {
  mode: dev ? 'development' : 'production',
  devtool: dev ? 'cheap-module-eval-source-map' : 'source-map',
  entry: {
    app: './src/base/main.js',
  },
  devServer: {
    host: process.env.HOST || 'localhost',
    port: 8080,
    overlay: {
      warnings: false,
      errors: true,
    },
    historyApiFallback: true,
    proxy: proxyTable,
    contentBase: resolve(__dirname, '../src'),
    stats: {
      errorDetails: true,
    },
  },
  output: {
    path: resolve(__dirname, '../dist'),
    publicPath: dev ? '/' : '',
    filename: 'assets/js/[name].[hash].js',
    chunkFilename: 'assets/js/[id].[chunkhash].js',
    pathinfo: false,
  },
  node: false,
  resolve: {
    extensions: [
      '.js',
      '.vue',
      '.json',
    ],
    modules: [
      resolve('src'),
      'node_modules',
    ],
    alias: {
      '@': resolve(__dirname, '../src'),
      '>': resolve(__dirname, '../test'),
      variables: resolve(__dirname, '../src/base/style/quasar.variables.styl'),
      slidetoggle: resolve(__dirname, '../src/base/style/karrot.slidetoggle.styl'),
      editbox: resolve(__dirname, '../src/base/style/karrot.editbox.styl'),
    },
    symlinks: false,
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(vue|js)$/,
        loader: 'eslint-loader',
        include: projectRoot,
        exclude: /node_modules/,
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          join(projectRoot, 'src'),
          /quasar\/src/,
          /\.mjs$/,
          /\.es6.js$/,
          /\.esm.js$/,
        ],
      },
      {
        test: /\.vue$/,
        exclude: /(node_modules)/,
        use: 'vue-loader',
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: (dev ? '' : '/') + 'assets/images',
          publicPath: cordova ? '../images' : '',
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: (dev ? '' : '/') + 'assets/fonts',
          publicPath: cordova ? '../fonts' : '',
        },
      },
      {
        test: /\.css$/,
        use: styleLoaders,
      },
      {
        test: /\.(stylus|styl)$/,
        use: [
          ...styleLoaders,
          'stylus-loader',
        ],
      },
    ],
  },
  plugins: [
    new StyleLintPlugin({
      files: ['src/**/*.{vue,styl}'],
      customSyntax: join(projectRoot, 'build/stylelintCustomSyntax.js'),
    }),
    new webpack.DefinePlugin(appEnv),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/base/index.html',
      minify: false,
      cordova,
    }),
    new PreloadWebpackPlugin({
      fileWhitelist: [/\.woff?$/],
      include: 'allAssets',
      rel: 'prefetch',
    }),
    new VueLoaderPlugin(),
    ...(dev ? [
      new HardSourceWebpackPlugin({
        configHash: function (webpackConfig) {
          return require('node-object-hash')({ sort: false }).hash([
            webpackConfig,
            appEnv,
          ])
        },
      }),
    ] : [
      new MiniCssExtractPlugin({
        filename: 'assets/css/[contenthash].css',
      }),
      new BundleAnalyzerPlugin({
        analyzerMode: cordova ? 'disabled' : 'static',
        reportFilename: 'bundlesize.html',
        defaultSizes: 'gzip',
        openAnalyzer: false,
        generateStatsFile: false,
        statsFilename: 'stats.json',
        statsOptions: null,
        logLevel: 'info',
      }),
    ]),
  ],
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new OptimizeCSSAssetsPlugin({}),
    ],
    splitChunks: {
      chunks: 'all',
      minChunks: 2,
      name: dev,
    },
    runtimeChunk: 'single',
  },
}
<commit_msg>try more chunk splitting to improve caching and size<commit_after>const webpack = require('webpack')
const { resolve, join } = require('path')
const projectRoot = resolve(__dirname, '../')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const PreloadWebpackPlugin = require('preload-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const StyleLintPlugin = require('stylelint-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const TerserPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

const dev = process.env.NODE_ENV !== 'production'
const cordova = process.env.CORDOVA === 'true'
const { backend, proxyTable } = require('./config')

const appEnv = {
  // define the karrot environment
  __ENV: {
    DEV: dev,
    CORDOVA: cordova,
    BACKEND: JSON.stringify(backend),
    KARROT_THEME: JSON.stringify(process.env.KARROT_THEME),
    FCM_CONFIG: JSON.stringify(process.env.FCM_CONFIG),
    SENTRY_CONFIG: JSON.stringify(process.env.SENTRY_CONFIG),
    GIT_SHA1: JSON.stringify(process.env.GIT_SHA1 || process.env.CIRCLE_SHA1),
  },
  // vuelidate wants this
  // see https://github.com/monterail/vuelidate/issues/365
  'process.env.BUILD': JSON.stringify('web'),
}

const styleLoaders = [
  dev ? 'style-loader' : MiniCssExtractPlugin.loader,
  {
    loader: 'css-loader',
    options: { importLoaders: 1 },
  },
  {
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: loader => [
        require('autoprefixer')(),
      ],
    },
  },
]

module.exports = {
  mode: dev ? 'development' : 'production',
  devtool: dev ? 'cheap-module-eval-source-map' : 'source-map',
  entry: {
    app: './src/base/main.js',
  },
  devServer: {
    host: process.env.HOST || 'localhost',
    port: 8080,
    overlay: {
      warnings: false,
      errors: true,
    },
    historyApiFallback: true,
    proxy: proxyTable,
    contentBase: resolve(__dirname, '../src'),
    stats: {
      errorDetails: true,
    },
  },
  output: {
    path: resolve(__dirname, '../dist'),
    publicPath: dev ? '/' : '',
    filename: 'assets/js/[name].[hash].js',
    chunkFilename: 'assets/js/[name].[contenthash].js',
    chunkLoadTimeout: 30000, // 30 seconds
    pathinfo: false,
  },
  node: false,
  resolve: {
    extensions: [
      '.js',
      '.vue',
      '.json',
    ],
    modules: [
      resolve('src'),
      'node_modules',
    ],
    alias: {
      '@': resolve(__dirname, '../src'),
      '>': resolve(__dirname, '../test'),
      variables: resolve(__dirname, '../src/base/style/quasar.variables.styl'),
      slidetoggle: resolve(__dirname, '../src/base/style/karrot.slidetoggle.styl'),
      editbox: resolve(__dirname, '../src/base/style/karrot.editbox.styl'),
    },
    symlinks: false,
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(vue|js)$/,
        loader: 'eslint-loader',
        include: projectRoot,
        exclude: /node_modules/,
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          join(projectRoot, 'src'),
          /quasar\/src/,
          /\.mjs$/,
          /\.es6.js$/,
          /\.esm.js$/,
        ],
      },
      {
        test: /\.vue$/,
        exclude: /(node_modules)/,
        use: 'vue-loader',
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: (dev ? '' : '/') + 'assets/images',
          publicPath: cordova ? '../images' : '',
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[hash:7].[ext]',
          outputPath: (dev ? '' : '/') + 'assets/fonts',
          publicPath: cordova ? '../fonts' : '',
        },
      },
      {
        test: /\.css$/,
        use: styleLoaders,
      },
      {
        test: /\.(stylus|styl)$/,
        use: [
          ...styleLoaders,
          'stylus-loader',
        ],
      },
    ],
  },
  plugins: [
    new StyleLintPlugin({
      files: ['src/**/*.{vue,styl}'],
      customSyntax: join(projectRoot, 'build/stylelintCustomSyntax.js'),
    }),
    new webpack.DefinePlugin(appEnv),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/base/index.html',
      minify: false,
      cordova,
    }),
    new PreloadWebpackPlugin({
      fileWhitelist: [/\.woff?$/],
      include: 'allAssets',
      rel: 'prefetch',
    }),
    new VueLoaderPlugin(),
    ...(dev ? [
      new HardSourceWebpackPlugin({
        configHash: function (webpackConfig) {
          return require('node-object-hash')({ sort: false }).hash([
            webpackConfig,
            appEnv,
          ])
        },
      }),
    ] : [
      new webpack.optimize.MinChunkSizePlugin({
        minChunkSize: 1000,
      }),
      new MiniCssExtractPlugin({
        filename: 'assets/css/[contenthash].css',
      }),
      new BundleAnalyzerPlugin({
        analyzerMode: cordova ? 'disabled' : 'static',
        reportFilename: 'bundlesize.html',
        defaultSizes: 'gzip',
        openAnalyzer: false,
        generateStatsFile: false,
        statsFilename: 'stats.json',
        statsOptions: null,
        logLevel: 'info',
      }),
    ]),
  ],
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new OptimizeCSSAssetsPlugin({}),
    ],
    splitChunks: {
      chunks: 'all',
      minChunks: 1,
      minSize: 0,
      maxAsyncRequests: Infinity,
      maxInitialRequests: Infinity,
      name: dev,
    },
    runtimeChunk: 'single',
  },
}
<|endoftext|>


<commit_before>const assert = require( 'assert' );
const path = require( 'path' );
const sander = require( 'sander' );

exports.compareError = compareError;
exports.compareWarnings = compareWarnings;
exports.deindent = deindent;
exports.executeBundle = executeBundle;
exports.extend = extend;
exports.loadConfig = loadConfig;
exports.loader = loader;
exports.normaliseOutput = normaliseOutput;

function compareError ( actual, expected ) {
	delete actual.stack;
	actual = Object.assign( {}, actual, {
		message: actual.message
	});

	if ( actual.frame ) {
		actual.frame = actual.frame.replace( /\s+$/gm, '' );
	}

	if ( expected.frame ) {
		expected.frame = deindent( expected.frame );
	}

	assert.deepEqual( actual, expected );
}

function compareWarnings ( actual, expected ) {
	assert.deepEqual(
		actual.map( warning => {
			const clone = Object.assign( {}, warning );
			delete clone.toString;

			if ( clone.frame ) {
				clone.frame = clone.frame.replace( /\s+$/gm, '' );
			}

			return clone;
		}),
		expected.map( warning => {
			if ( warning.frame ) {
				warning.frame = deindent( warning.frame );
			}
			return warning;
		})
	);
}

function deindent(str) {
	return str.slice(1).replace(/^\t+/gm, '').replace(/\s+$/gm, '').trim();
}

function executeBundle(bundle) {
	return bundle
		.generate({
			format: 'cjs'
		})
		.then(cjs => {
			const m = new Function('module', 'exports', cjs.code);

			const module = { exports: {} };
			m(module, module.exports);

			return module.exports;
		});
}

function extend(target) {
	[].slice.call(arguments, 1).forEach(source => {
		source &&
			Object.keys(source).forEach(key => {
				target[key] = source[key];
			});
	});

	return target;
}

function loadConfig ( configFile ) {
	try {
		return require( configFile );
	} catch ( err ) {
		if ( err.code === 'MODULE_NOT_FOUND' ) {
			const dir = path.dirname( configFile );
			console.warn( `Test configuration ${configFile} not found.\nRemoving directory as this test probably no longer exists.` );
			sander.rimrafSync( dir );
		} else {
			throw new Error( `Failed to load ${path}: ${err.message}` );
		}
	}
}

function loader(modules) {
	return {
		resolveId(id) {
			return id in modules ? id : null;
		},

		load(id) {
			return modules[id];
		}
	};
}

function normaliseOutput(code) {
	return code.toString().trim().replace(/\r\n/g, '\n');
}
<commit_msg>Make sure tests are only removed if they contain at most an "_actual" directory<commit_after>const assert = require( 'assert' );
const path = require( 'path' );
const sander = require( 'sander' );

exports.compareError = compareError;
exports.compareWarnings = compareWarnings;
exports.deindent = deindent;
exports.executeBundle = executeBundle;
exports.extend = extend;
exports.loadConfig = loadConfig;
exports.loader = loader;
exports.normaliseOutput = normaliseOutput;

function compareError ( actual, expected ) {
	delete actual.stack;
	actual = Object.assign( {}, actual, {
		message: actual.message
	});

	if ( actual.frame ) {
		actual.frame = actual.frame.replace( /\s+$/gm, '' );
	}

	if ( expected.frame ) {
		expected.frame = deindent( expected.frame );
	}

	assert.deepEqual( actual, expected );
}

function compareWarnings ( actual, expected ) {
	assert.deepEqual(
		actual.map( warning => {
			const clone = Object.assign( {}, warning );
			delete clone.toString;

			if ( clone.frame ) {
				clone.frame = clone.frame.replace( /\s+$/gm, '' );
			}

			return clone;
		}),
		expected.map( warning => {
			if ( warning.frame ) {
				warning.frame = deindent( warning.frame );
			}
			return warning;
		})
	);
}

function deindent(str) {
	return str.slice(1).replace(/^\t+/gm, '').replace(/\s+$/gm, '').trim();
}

function executeBundle(bundle) {
	return bundle
		.generate({
			format: 'cjs'
		})
		.then(cjs => {
			const m = new Function('module', 'exports', cjs.code);

			const module = { exports: {} };
			m(module, module.exports);

			return module.exports;
		});
}

function extend(target) {
	[].slice.call(arguments, 1).forEach(source => {
		source &&
			Object.keys(source).forEach(key => {
				target[key] = source[key];
			});
	});

	return target;
}

function loadConfig ( configFile ) {
	try {
		return require( configFile );
	} catch ( err ) {
		if ( err.code === 'MODULE_NOT_FOUND' ) {
			const dir = path.dirname( configFile );
			console.warn( `Test configuration ${configFile} not found.\nTrying to clean up no longer existing test...` );
			sander.rimrafSync( path.join( dir, '_actual' ) );
			sander.rmdirSync( dir );
			console.warn( 'Directory removed.' );
		} else {
			throw new Error( `Failed to load ${path}: ${err.message}` );
		}
	}
}

function loader(modules) {
	return {
		resolveId(id) {
			return id in modules ? id : null;
		},

		load(id) {
			return modules[id];
		}
	};
}

function normaliseOutput(code) {
	return code.toString().trim().replace(/\r\n/g, '\n');
}
<|endoftext|>