Started to refactor JS Plugin class

Start with making plugin.js into a module, and re-implementing Plugin
using the class-based ES6 syntax, as it's a lot cleaner. Dispatch
events from setters instead of using Object.observe when changing
values that prompt external changes.

Vulcanize's whitespace stripper doesn't like the ES6 syntax though, so
disable that.

Also start implementing some tests using Mocha and should.js.

Finish refactoring JS Plugin code
This commit is contained in:
Oliver Hamlet
2015-12-23 16:11:34 +00:00
parent dd77312133
commit 56a7170d1c
12 changed files with 607 additions and 192 deletions
+2
View File
@@ -17,6 +17,8 @@
"eslint": "^1.10.3",
"eslint-config-airbnb": "^2.0.0",
"fs-extra": "^0.26.2",
"mocha": "^2.3.4",
"should": "^8.0.1",
"vulcanize": "^0.7.11"
}
}
-1
View File
@@ -34,7 +34,6 @@ for (var i = 0; i < release_paths.length; ++i) {
child_process.execFileSync(vulcanize, [
'--inline',
'--strip',
'--config',
path.join(root_path, 'scripts', 'vulcanize.config.json'),
'-o',
+12 -6
View File
@@ -220,7 +220,7 @@ loot-clear-metadata
observe: {
'data.tags': 'onTagsChange',
'data.computed.messages': 'onMessagesChange',
'data.messages': 'onMessagesChange',
'data.userlist': 'onUserlistChange',
'data.isSearchResult' : 'onSearchResultChange',
'data.isMenuOpen': 'onMenuToggle'
@@ -278,8 +278,8 @@ loot-clear-metadata
onTagsChange: function(oldValue, newValue) {
if (this.data) {
this.getElementsByClassName('tag add')[0].classList.toggle('hidden', this.data.computed.tags.added.length == 0);
this.getElementsByClassName('tag remove')[0].classList.toggle('hidden', this.data.computed.tags.removed.length == 0);
this.getElementsByClassName('tag add')[0].classList.toggle('hidden', this.data.tagStrings.added.length == 0);
this.getElementsByClassName('tag remove')[0].classList.toggle('hidden', this.data.tagStrings.removed.length == 0);
}
},
@@ -291,10 +291,16 @@ loot-clear-metadata
messageUL.removeChild(messageUL.firstElementChild);
}
/* Now add new messages. */
this.data.computed.messages.forEach(function(message){
messageUL.appendChild(message);
var visibleMessages = filters.applyMessageFilters(this.data.messages);
visibleMessages.forEach(function(message) {
var messageLi = document.createElement('li');
messageLi.className = message.type;
// Use the Marked library for Markdown formatting support.
messageLi.innerHTML = marked(message.content[0].str);
messageUL.appendChild(messageLi);
});
messageUL.classList.toggle('hidden', this.data.computed.messages.length == 0);
messageUL.classList.toggle('hidden', visibleMessages.length == 0);
}
},
@@ -126,7 +126,7 @@ loot-editor-close
<div flex>
<h1>{{data.name}}</h1>
<span id="version">{{data.version}}</span>
<span id="crc">{{data.computed.crc}}</span>
<span id="crc">{{data.crcString}}</span>
</div>
<core-tooltip id="isMaster" label="Master File" noarrow>
<core-icon icon="loot-custom-icons:crown"></core-icon>
@@ -349,7 +349,7 @@ loot-editor-close
plugin.userlist.msg = rowsData;
} else if (tables[j].parentElement.id == 'tags') {
rowsData.forEach(function(value, index, arr){
arr[index] = oldData.convTagObj(value);
arr[index] = Plugin.tagFromRowData(value);
});
plugin.userlist.tag = rowsData;
} else if (tables[j].parentElement.id == 'dirty') {
@@ -493,14 +493,14 @@ loot-editor-close
if (newData.masterlist && newData.masterlist.tag) {
newData.masterlist.tag.forEach(function(tag) {
var tagData = newData.convTagObj(tag);
var tagData = Plugin.tagToRowData(tag);
var row = tables[j].addRow(tagData);
tables[j].setReadOnly(row);
}, newData);
}
if (tempData.userlist && tempData.userlist.tag) {
tempData.userlist.tag.forEach(function(tag) {
var tagData = Plugin.prototype.convTagObj(tag);
var tagData = Plugin.tagToRowData(tag);
tables[j].addRow(tagData);
}, tempData);
}
+5 -4
View File
@@ -137,7 +137,7 @@ searchTarget is the ID of the core-list element to search the elements of.
document.getElementById(host.searchTarget).data.forEach(function(plugin, index){
if (plugin.name.toLowerCase().indexOf(needle) != -1
|| (!versionHidden && plugin.version.toLowerCase().indexOf(needle) != -1)
|| (!crcHidden && plugin.getCrcString().toLowerCase().indexOf(needle) != -1)) {
|| (!crcHidden && plugin.crcString.toLowerCase().indexOf(needle) != -1)) {
host.results.push(index);
plugin.isSearchResult = true;
@@ -145,7 +145,7 @@ searchTarget is the ID of the core-list element to search the elements of.
}
if (!bashTagHidden) {
var tags = plugin.getTagStrings();
var tags = plugin.tagStrings;
if (tags.added.toLowerCase().indexOf(needle) != -1
|| tags.removed.toLowerCase().indexOf(needle) != -1) {
@@ -155,8 +155,9 @@ searchTarget is the ID of the core-list element to search the elements of.
}
}
for (var i = 0; i < plugin.computed.messages.length; ++i) {
if (plugin.computed.messages[i].textContent.toLowerCase().indexOf(needle) != -1) {
var visibleMessages = filters.applyMessageFilters(this.data.messages);
for (var i = 0; i < visibleMessages.length; ++i) {
if (visibleMessages[i].content[0].str.toLowerCase().indexOf(needle) != -1) {
host.results.push(index);
plugin.isSearchResult = true;
return;
+4 -4
View File
@@ -214,9 +214,9 @@
<loot-plugin-card data="{{model}}" id="{{model.id}}" data-active="{{model.isActive}}" data-empty="{{model.isEmpty}}" data-archive="{{model.loadsArchive}}" data-master="{{model.isMaster}}">
<h1>{{model.name}}</h1>
<span class="version">{{model.version}}</span>
<span class="crc">{{model.computed.crc}}</span>
<span class="tag add">{{model.computed.tags.added}}</span>
<span class="tag remove">{{model.computed.tags.removed}}</span>
<span class="crc">{{model.crcString}}</span>
<span class="tag add">{{model.tagStrings.added}}</span>
<span class="tag remove">{{model.tagStrings.removed}}</span>
<ul></ul>
</loot-plugin-card>
</template>
@@ -316,9 +316,9 @@
<script src="../../../bower_components/Jed/jed.js"></script>
<script src="../../../bower_components/jed-gettext-parser/jedGettextParser.js"></script>
<script src="js/l10n.js"></script>
<script src="js/plugin.js"></script>
<script src="js/helpers.js"></script>
<script src="js/loot.js"></script>
<script src="js/plugin.js"></script>
<script src="js/filters.js"></script>
<script src="js/events.js"></script>
<script src="js/init.js"></script>
+21 -3
View File
@@ -1,3 +1,17 @@
'use strict';
function onPluginMessageChange(evt) {
document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + evt.detail.totalDiff;
document.getElementById('totalMessageNo').textContent = parseInt(document.getElementById('totalMessageNo').textContent, 10) + evt.detail.totalDiff;
document.getElementById('totalWarningNo').textContent = parseInt(document.getElementById('totalWarningNo').textContent, 10) + evt.detail.warningDiff;
document.getElementById('totalErrorNo').textContent = parseInt(document.getElementById('totalErrorNo').textContent, 10) + evt.detail.errorDiff;
}
function onPluginIsDirtyChange(evt) {
if (evt.detail.isDirty) {
document.getElementById('dirtyPluginNo').textContent = parseInt(document.getElementById('dirtyPluginNo').textContent, 10) + 1;
} else {
document.getElementById('dirtyPluginNo').textContent = parseInt(document.getElementById('dirtyPluginNo').textContent, 10) - 1;
}
}
function saveFilterState(evt) {
var request = JSON.stringify({
name: 'saveFilterState',
@@ -62,7 +76,7 @@ function onChangeGame(evt) {
/* Parse the data sent from C++. */
try {
var gameInfo = JSON.parse(result, jsonToPlugin);
var gameInfo = JSON.parse(result, loot.Plugin.fromJson);
loot.game.folder = gameInfo.folder;
loot.game.masterlist = gameInfo.masterlist;
loot.game.globalMessages = gameInfo.globalMessages;
@@ -163,7 +177,7 @@ function onSortPlugins(evt) {
}
}
if (!found) {
loot.game.plugins.push(new Plugin(plugin));
loot.game.plugins.push(new loot.Plugin(plugin));
loot.game.loadOrder.push(loot.game.plugins[loot.game.plugins.length - 1]);
}
});
@@ -668,7 +682,7 @@ function onContentRefresh(evt) {
}
if (!foundPlugin) {
/* A new plugin. */
loot.game.plugins.push(new Plugin(plugin));
loot.game.plugins.push(new loot.Plugin(plugin));
}
pluginNames.push(plugin.name);
});
@@ -760,4 +774,8 @@ function setupEventHandlers() {
document.getElementById('cardsNav').addEventListener('click', onSidebarClick, false);
document.getElementById('cardsNav').addEventListener('dblclick', onSidebarClick, false);
/* Set up handler for plugin message and dirty info changes. */
document.addEventListener('loot-plugin-message-change', onPluginMessageChange);
document.addEventListener('loot-plugin-isdirty-change', onPluginIsDirtyChange);
}
+6 -4
View File
@@ -12,13 +12,13 @@ var filters = {
}
if (plugin.name.toLowerCase().indexOf(needle) != -1
|| plugin.getCrcString().toLowerCase().indexOf(needle) != -1
|| plugin.crcString.toLowerCase().indexOf(needle) != -1
|| plugin.version.toLowerCase().indexOf(needle) != -1) {
return true;
}
var tags = plugin.getTagStrings();
var tags = plugin.tagStrings;
if (tags.added.toLowerCase().indexOf(needle) != -1
|| tags.removed.toLowerCase().indexOf(needle) != -1) {
@@ -202,9 +202,11 @@ function setFilteredUIData() {
document.getElementById('cardsNav').data = filtered;
document.getElementById('main').lastElementChild.data = filtered;
/* Also run message filters on the filtered plugins. */
filtered.forEach(function(plugin){
plugin.computed.messages = plugin.getUIMessages();
var element = document.getElementById(plugin.id);
if (element) {
element.onMessagesChange();
}
});
/* Now perform search again. If there is no current search, this won't
+1 -1
View File
@@ -155,7 +155,7 @@ function initVars() {
});
} else {
return loot.query('getGameData').then(function(result){
var game = JSON.parse(result, jsonToPlugin);
var game = JSON.parse(result, loot.Plugin.fromJson);
loot.game.folder = game.folder;
loot.game.masterlist = game.masterlist;
loot.game.globalMessages = game.globalMessages;
+169 -165
View File
@@ -22,193 +22,197 @@
<http://www.gnu.org/licenses/>.
*/
'use strict';
/* Plugin object for managing data and UI interaction. */
function Plugin(obj) {
this.name = obj.name;
this.crc = obj.crc;
this.version = obj.version;
this.isActive = obj.isActive;
this.isEmpty = obj.isEmpty;
this.isMaster = obj.isMaster;
this.loadsArchive = obj.loadsArchive;
(function exportModule(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else {
// Browser globals
root.loot = root.loot || {};
root.loot.Plugin = factory();
}
}(this, () => {
return class Plugin {
constructor(obj) {
/* Plugin data */
this.name = obj.name;
this.crc = obj.crc;
this.version = obj.version;
this.isActive = obj.isActive;
this.isEmpty = obj.isEmpty;
this.isMaster = obj.isMaster;
this.loadsArchive = obj.loadsArchive;
this.masterlist = obj.masterlist;
this.userlist = obj.userlist;
this.masterlist = obj.masterlist;
this.userlist = obj.userlist;
this.modPriority = obj.modPriority;
this.isGlobalPriority = obj.isGlobalPriority;
this.messages = obj.messages;
this.tags = obj.tags;
this.isDirty = obj.isDirty;
this.modPriority = obj.modPriority;
this.isGlobalPriority = obj.isGlobalPriority;
this._messages = obj.messages;
this.tags = obj.tags;
this._isDirty = obj.isDirty || false;
this.id = this.name.replace(/\s+/g, '');
this.isMenuOpen = false;
this.isEditorOpen = false;
this.isConflictFilterChecked = false;
this.isSearchResult = false;
/* Converts between the LOOT metadata object for tags, and their
editor row representation. */
Plugin.prototype.convTagObj = function(tag) {
var newTag = {
condition: tag.condition
};
if (tag.type) {
/* Input is row data. */
if (tag.type == 'remove') {
newTag.name = '-' + tag.name;
} else {
newTag.name = tag.name;
}
} else {
/* Input is metadata object. */
if (tag.name[0] == '-') {
newTag.type = 'remove';
newTag.name = tag.name.substr(1);
} else {
newTag.type = 'add';
newTag.name = tag.name;
}
}
return newTag;
/* UI state variables */
this.id = this.name.replace(/\s+/g, '');
this.isMenuOpen = false;
this.isEditorOpen = false;
this.isConflictFilterChecked = false;
this.isSearchResult = false;
}
Plugin.prototype.getTagStrings = function() {
var tagsAdded = [];
var tagsRemoved = [];
if (this.tags) {
for (var i = 0; i < this.tags.length; ++i) {
if (this.tags[i].name[0] == '-') {
tagsRemoved.push(this.tags[i].name.substr(1));
} else {
tagsAdded.push(this.tags[i].name);
}
}
}
/* Now make sure that the same tag doesn't appear in both arrays.
Prefer the removed list. */
for (var i = 0; i < tagsAdded.length; ++i) {
for (var j = 0; j < tagsRemoved.length; ++j) {
if (tagsRemoved[j].toLowerCase() == tagsAdded[i].toLowerCase()) {
/* Remove tag from the tagsAdded array. */
tagsAdded.splice(i, 1);
--i;
}
}
}
return {
added: tagsAdded.join(', '),
removed: tagsRemoved.join(', ')
};
static fromJson(key, value) {
if (value !== null && value.__type === 'Plugin') {
return new Plugin(value);
}
return value;
}
Plugin.prototype.getPriorityString = function() {
if (this.modPriority != 0) {
return this.modPriority.toString();
} else {
return '';
}
static tagFromRowData(rowData) {
if (rowData.condition === undefined || rowData.name === undefined || rowData.type === undefined) {
throw new TypeError('Row data members are undefined');
}
const tag = {
condition: rowData.condition,
name: '',
};
if (rowData.type === 'remove') {
tag.name = '-';
}
tag.name += rowData.name;
return tag;
}
Plugin.prototype.getCrcString = function() {
if (this.crc == 0) {
return '';
} else {
/* Pad CRC string to 8 characters. */
return ('00000000' + this.crc.toString(16).toUpperCase()).slice(-8);
}
static tagToRowData(tag) {
const rowData = {
condition: tag.condition,
};
if (tag.name[0] === '-') {
rowData.type = 'remove';
rowData.name = tag.name.substr(1);
} else {
rowData.type = 'add';
rowData.name = tag.name;
}
return rowData;
}
Plugin.prototype.getUIMessages = function() {
var uiMessages = [];
/* Now add the new messages. */
if (this.messages && this.messages.length != 0) {
filters.applyMessageFilters(this.messages).forEach(function(message) {
var messageLi = document.createElement('li');
messageLi.className = message.type;
// Use the Marked library for Markdown formatting support.
messageLi.innerHTML = marked(message.content[0].str);
uiMessages.push(messageLi);
get tagStrings() {
const tagsAdded = [];
const tagsRemoved = [];
});
if (this.tags) {
for (let i = 0; i < this.tags.length; ++i) {
if (this.tags[i].name[0] === '-') {
tagsRemoved.push(this.tags[i].name.substr(1));
} else {
tagsAdded.push(this.tags[i].name);
}
}
}
/* Now make sure that the same tag doesn't appear in both arrays.
Prefer the removed list. */
for (let i = 0; i < tagsAdded.length; ++i) {
for (let j = 0; j < tagsRemoved.length; ++j) {
if (tagsRemoved[j].toLowerCase() === tagsAdded[i].toLowerCase()) {
/* Remove tag from the tagsAdded array. */
tagsAdded.splice(i, 1);
--i;
}
}
}
return uiMessages;
return {
added: tagsAdded.join(', '),
removed: tagsRemoved.join(', '),
};
}
Plugin.prototype.observer = function(changes) {
changes.forEach(function(change) {
if (change.name == 'tags') {
change.object.computed.tags = change.object.getTagStrings();
} else if (change.name == 'modPriority') {
change.object.computed.priority = change.object.getPriorityString();
} else if (change.name == 'crc') {
change.object.computed.crc = change.object.getCrcString();
} else if (change.name == 'messages') {
/* Update computed list items. */
change.object.computed.messages = change.object.getUIMessages();
get priorityString() {
if (this.modPriority === undefined || this.modPriority === 0) {
return '';
}
/* Update the message counts. */
var oldTotal = 0;
var newTotal = 0;
var oldWarns = 0;
var newWarns = 0;
var oldErrs = 0;
var newErrs = 0;
return this.modPriority.toString();
}
if (change.oldValue) {
oldTotal = change.oldValue.length;
get crcString() {
if (this.crc === undefined || this.crc === 0) {
return '';
}
change.oldValue.forEach(function(message){
if (message.type == 'warn') {
++oldWarns;
} else if (message.type == 'error') {
++oldErrs;
}
});
}
if (change.object[change.name]) {
newTotal = change.object[change.name].length;
/* Pad CRC string to 8 characters. */
return ('00000000' + this.crc.toString(16).toUpperCase()).slice(-8);
}
change.object[change.name].forEach(function(message){
if (message.type == 'warn') {
++newWarns;
} else if (message.type == 'error') {
++newErrs;
}
});
}
get messages() {
return this._messages;
}
document.getElementById('filterTotalMessageNo').textContent = parseInt(document.getElementById('filterTotalMessageNo').textContent, 10) + newTotal - oldTotal;
document.getElementById('totalMessageNo').textContent = parseInt(document.getElementById('totalMessageNo').textContent, 10) + newTotal - oldTotal;
document.getElementById('totalWarningNo').textContent = parseInt(document.getElementById('totalWarningNo').textContent, 10) + newWarns - oldWarns;
document.getElementById('totalErrorNo').textContent = parseInt(document.getElementById('totalErrorNo').textContent, 10) + newErrs - oldErrs;
} else if (change.name == 'isDirty') {
/* Update dirty counts. */
if (change.object[change.name]) {
document.getElementById('dirtyPluginNo').textContent = parseInt(document.getElementById('dirtyPluginNo').textContent, 10) + 1;
} else {
document.getElementById('dirtyPluginNo').textContent = parseInt(document.getElementById('dirtyPluginNo').textContent, 10) - 1;
}
}
set messages(messages) {
/* Update the message counts. */
let oldTotal = 0;
let newTotal = 0;
let oldWarns = 0;
let newWarns = 0;
let oldErrs = 0;
let newErrs = 0;
if (this._messages) {
oldTotal = this._messages.length;
this._messages.forEach((message) => {
if (message.type === 'warn') {
++oldWarns;
} else if (message.type === 'error') {
++oldErrs;
}
});
}
if (messages) {
newTotal = messages.length;
messages.forEach((message) => {
if (message.type === 'warn') {
++newWarns;
} else if (message.type === 'error') {
++newErrs;
}
});
}
if (newTotal !== oldTotal || newWarns !== oldWarns || newErrs !== oldErrs) {
document.dispatchEvent(new CustomEvent('loot-plugin-message-change', {
detail: {
totalDiff: newTotal - oldTotal,
warningDiff: newWarns - oldWarns,
errorDiff: newErrs - oldErrs,
},
}));
}
this._messages = messages;
}
this.computed = {
tags: this.getTagStrings(),
priority: this.getPriorityString(),
crc: this.getCrcString(),
messages: this.getUIMessages(),
};
Object.observe(this, this.observer);
}
function jsonToPlugin(key, value) {
if (value !== null && value.__type === 'Plugin') {
var p = new Plugin(value);
return p;
get isDirty() {
return this._isDirty;
}
return value;
}
set isDirty(dirty) {
/* Update dirty counts. */
if (dirty !== this._isDirty) {
document.dispatchEvent(new CustomEvent('loot-plugin-isdirty-change', {
detail: {
isDirty: dirty,
},
}));
}
this._isDirty = dirty;
}
};
}));
+18
View File
@@ -0,0 +1,18 @@
<meta charset="utf-8">
<title>Mocha Tests</title>
<link href="../../../../../node_modules/mocha/mocha.css" rel="stylesheet" />
<div id="mocha"></div>
<script src="../../../../../node_modules/should/should.js"></script>
<script src="../../../../../node_modules/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="../../../../gui/html/js/plugin.js"></script>
<script src="test_plugin.js"></script>
<script>
mocha.checkLeaks();
mocha.run();
</script>
</body>
</html>
+365
View File
@@ -0,0 +1,365 @@
'use strict';
describe('Plugin', () => {
describe('#Plugin()', () => {
it('should throw if nothing is passed', () => {
(() => { new loot.Plugin(); }).should.throw();
});
it('should throw if an object with no name key is passed', () => {
(() => { new loot.Plugin({}); }).should.throw();
});
it('should not throw if some members are undefined', () => {
(() => { new loot.Plugin({ name: 'test' }); }).should.not.throw();
});
});
describe('#fromJson()', () => {
it('should return the value object if the JSON is not of the Plugin type', () => {
const testInputObj = {
name: 'test',
crc: 0xDEADBEEF,
};
const testInputJson = JSON.stringify(testInputObj);
JSON.parse(testInputJson, loot.Plugin.fromJson).should.deepEqual(testInputObj);
});
it('should return a Plugin object if the JSON is of the Plugin type', () => {
const testInputObj = {
name: 'test',
crc: 0xDEADBEEF,
__type: 'Plugin',
};
const testInputJson = JSON.stringify(testInputObj);
JSON.parse(testInputJson, loot.Plugin.fromJson).should.be.instanceof(loot.Plugin);
});
});
describe('#tagFromRowData()', () => {
it('should throw if passed nothing', () => {
(() => { loot.Plugin.tagFromRowData(); }).should.throw();
});
it('should return an empty object if passed nothing', () => {
(() => { loot.Plugin.tagFromRowData({}); }).should.throw();
});
it('should return a raw metadata object if passed a row data object that removes a tag', () => {
loot.Plugin.tagFromRowData({
condition: 'foo',
type: 'remove',
name: 'bar',
}).should.deepEqual({
condition: 'foo',
name: '-bar',
});
});
it('should return a raw metadata object if passed a row data object that adds a tag', () => {
loot.Plugin.tagFromRowData({
condition: 'foo',
type: 'add',
name: 'bar',
}).should.deepEqual({
condition: 'foo',
name: 'bar',
});
});
});
describe('#tagToRowData()', () => {
it('should throw if passed nothing', () => {
(() => { loot.Plugin.tagToRowData(); }).should.throw();
});
it('should return an empty object if passed nothing', () => {
(() => { loot.Plugin.tagToRowData({}); }).should.throw();
});
it('should return a row data object if passed a raw metadata object that removes a tag', () => {
loot.Plugin.tagToRowData({
condition: 'foo',
name: '-bar',
}).should.deepEqual({
condition: 'foo',
type: 'remove',
name: 'bar',
});
});
it('should return a row data object if passed a raw metadata object that adds a tag', () => {
loot.Plugin.tagToRowData({
condition: 'foo',
name: 'bar',
}).should.deepEqual({
condition: 'foo',
type: 'add',
name: 'bar',
});
});
});
describe('#tagStrings', () => {
it('should return an object containing empty strings if no tags are set', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.tagStrings.should.deepEqual({
added: '',
removed: '',
});
});
it('should return an object containing strings of comma-separated tag names if tags are set', () => {
const plugin = new loot.Plugin({
name: 'test',
tags: [
{ name: 'Relev' },
{ name: 'Delev' },
{ name: 'Names' },
{ name: '-C.Climate' },
{ name: '-Actor.ABCS' },
],
});
plugin.tagStrings.should.deepEqual({
added: 'Relev, Delev, Names',
removed: 'C.Climate, Actor.ABCS',
});
});
it('should output a tag in the removed string if it appears as both added and removed', () => {
const plugin = new loot.Plugin({
name: 'test',
tags: [
{ name: 'Relev' },
{ name: '-Relev' },
],
});
plugin.tagStrings.should.deepEqual({
added: '',
removed: 'Relev',
});
});
});
describe('#priorityString', () => {
it('should return an empty string if priority is undefined', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.priorityString.should.equal('');
});
it('should return an empty string if priority is zero', () => {
const plugin = new loot.Plugin({
name: 'test',
modPriority: 0,
});
plugin.priorityString.should.equal('');
});
it('should return priority valueAs string if non zero', () => {
const plugin = new loot.Plugin({
name: 'test',
modPriority: -50,
});
plugin.priorityString.should.equal('-50');
});
});
describe('#crcString', () => {
it('should return an empty string if crc is undefined', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.crcString.should.equal('');
});
it('should return an empty string if crc is zero', () => {
const plugin = new loot.Plugin({
name: 'test',
crc: 0,
});
plugin.crcString.should.equal('');
});
it('should return crc value as string if non zero', () => {
const plugin = new loot.Plugin({
name: 'test',
crc: 0xDEADBEEF,
});
plugin.crcString.should.equal('DEADBEEF');
});
it('should pad crc value to eight digits', () => {
const plugin = new loot.Plugin({
name: 'test',
crc: 0xBEEF,
});
plugin.crcString.should.equal('0000BEEF');
});
});
describe('#messages', () => {
let handleEvent;
afterEach(() => {
document.removeEventListener('loot-plugin-message-change', handleEvent);
});
it('getting messages if they are undefined should return undefined', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.should.not.have.ownProperty('messages');
});
it('getting messages if the array is empty should return an empty array', () => {
const plugin = new loot.Plugin({
name: 'test',
messages: [],
});
plugin.messages.should.be.Array();
plugin.messages.should.be.empty();
});
it('getting messages should return any that are set', () => {
const messages = [{
type: 'say',
content: 'test message',
}];
const plugin = new loot.Plugin({
name: 'test',
messages: messages,
});
plugin.messages.should.be.deepEqual(messages);
});
it('setting messages should store any set', () => {
const plugin = new loot.Plugin({
name: 'test',
messages: [],
});
const messages = [{
type: 'say',
content: 'test message',
}];
plugin.messages = messages;
plugin.messages.should.be.deepEqual(messages);
});
it('setting messages should not fire an event if no message counts were changed', (done) => {
const plugin = new loot.Plugin({
name: 'test',
messages: [{
type: 'say',
content: 'test message',
}],
});
const messages = [{
type: 'say',
content: 'another test message',
}];
handleEvent = () => {
done(new Error('Should not have fired an event'));
};
document.addEventListener('loot-plugin-message-change', handleEvent);
plugin.messages = messages;
setTimeout(done, 100);
});
it('setting messages should fire an event if message counts were changed', (done) => {
const plugin = new loot.Plugin({
name: 'test',
messages: [],
});
const messages = [{
type: 'error',
content: 'test message',
}];
handleEvent = (evt) => {
evt.detail.totalDiff.should.equal(1);
evt.detail.warningDiff.should.equal(0);
evt.detail.errorDiff.should.equal(1);
done();
};
document.addEventListener('loot-plugin-message-change', handleEvent);
plugin.messages = messages;
});
});
describe('#isDirty', () => {
let handleEvent;
afterEach(() => {
document.removeEventListener('loot-plugin-isdirty-change', handleEvent);
});
it('getting value should return false if isDirty has not been set in the constructor', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.isDirty.should.be.false();
});
it('getting value should return true if isDirty is set to true in the constructor', () => {
const plugin = new loot.Plugin({
name: 'test',
isDirty: true,
});
plugin.isDirty.should.be.true();
});
it('setting value should store set value', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.isDirty = true;
plugin.isDirty.should.be.true();
});
it('setting value to the current value should not fire an event', (done) => {
const plugin = new loot.Plugin({ name: 'test' });
handleEvent = () => {
done(new Error('Should not have fired an event'));
};
document.addEventListener('loot-plugin-isdirty-change', handleEvent);
plugin.isDirty = plugin.isDirty;
setTimeout(done, 100);
});
it('setting value not equal to the current value should fire an event', (done) => {
const plugin = new loot.Plugin({ name: 'test' });
handleEvent = (evt) => {
evt.detail.isDirty.should.be.true();
done();
};
document.addEventListener('loot-plugin-isdirty-change', handleEvent);
plugin.isDirty = !plugin.isDirty;
});
});
});