From 08beb108a4387558d8b7126f20a9ca6b39869f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Dahlgren?= Date: Sat, 10 Jul 2021 15:54:10 +0200 Subject: [PATCH] Steam Workshop Mods --- app.js | 38 +++++++- config.js.example | 6 ++ lib/manager.js | 5 +- lib/mods.js | 10 +- lib/server.js | 24 ++++- lib/steam_mods.js | 96 +++++++++++++++++++ package.json | 1 + public/js/app/models/mod.js | 7 +- public/js/app/views/mods/download/form.js | 82 ++++++++++++++++ public/js/app/views/mods/list.js | 33 ++++++- public/js/app/views/mods/list_item.js | 4 + public/js/app/views/mods/search/form.js | 90 +++++++++++++++++ public/js/app/views/mods/search/list_item.js | 52 ++++++++++ public/js/app/views/servers/info.js | 11 +++ public/js/app/views/servers/mods/list_item.js | 2 +- public/js/app/views/servers/view.js | 2 +- public/js/tpl/mods/download/form.html | 11 +++ public/js/tpl/mods/list.html | 10 ++ public/js/tpl/mods/list_item.html | 16 +++- public/js/tpl/mods/search/form.html | 23 +++++ public/js/tpl/mods/search/list_item.html | 27 ++++++ public/js/tpl/servers/mods/list_item.html | 2 +- routes/mods.js | 21 ++++ test/lib/server.js | 4 +- test/lib/steam_mods.js | 21 ++++ 25 files changed, 574 insertions(+), 24 deletions(-) create mode 100644 lib/steam_mods.js create mode 100644 public/js/app/views/mods/download/form.js create mode 100644 public/js/app/views/mods/search/form.js create mode 100644 public/js/app/views/mods/search/list_item.js create mode 100644 public/js/tpl/mods/download/form.html create mode 100644 public/js/tpl/mods/search/form.html create mode 100644 public/js/tpl/mods/search/list_item.html create mode 100644 test/lib/steam_mods.js diff --git a/app.js b/app.js index e8ddd73..66f082e 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,5 @@ var express = require('express') +var fs = require('fs') var bodyParser = require('body-parser') var morgan = require('morgan') var path = require('path') @@ -11,7 +12,7 @@ var webpackConfig = require('./webpack.config') var setupBasicAuth = require('./lib/setup-basic-auth') var Manager = require('./lib/manager') var Missions = require('./lib/missions') -var Mods = require('./lib/mods') +var SteamMods = require('./lib/steam_mods') var Logs = require('./lib/logs') var Settings = require('./lib/settings') @@ -29,17 +30,44 @@ app.use(morgan(config.logFormat || 'dev')) app.use(serveStatic(path.join(__dirname, 'public'))) +/* +Workaround for Steam Workshop with Linux Arma server + +Absolute paths are not supported. +Create symlink in Arma folder to Workshop folder. +Rewrite Workshop mods to use relative path to symlinked folder instead. +*/ +if (config.type === 'linux' && config.steam && config.steam.path) { + var tempWorkshopFolder = path.join(config.path, 'workshop') + try { + var stat = fs.lstatSync(tempWorkshopFolder) + if (!stat.isSymbolicLink()) { + console.error('Please remove workshop folder from Arma directory manually and restart application') + process.exit(1) + } + fs.unlinkSync(tempWorkshopFolder) + } catch (err) { + if (err.code !== 'ENOENT') { + console.error('Something went wrong when creating workaround for workshop') + console.error(err) + process.exit(1) + } + } + + fs.symlinkSync(config.steam.path, tempWorkshopFolder) +} + var logs = new Logs(config) -var manager = new Manager(config, logs) -manager.load() - var missions = new Missions(config) -var mods = new Mods(config) +var mods = new SteamMods(config) mods.updateMods() var settings = new Settings(config) +var manager = new Manager(config, logs, mods) +manager.load() + app.use('/api/logs', require('./routes/logs')(logs)) app.use('/api/missions', require('./routes/missions')(missions)) app.use('/api/mods', require('./routes/mods')(mods)) diff --git a/config.js.example b/config.js.example index 2731db8..5666717 100644 --- a/config.js.example +++ b/config.js.example @@ -13,6 +13,12 @@ module.exports = { '@mod1', '@mod2', ], + steam: { + apiKey: '1234567890ABCDE', // https://steamcommunity.com/dev/apikey + path: 'path-to-main-steam-folder', + username: 'steam_username', + password: 'steam_password', + }, admins: [], // add steam IDs here to enable #login without password auth: { // If both username and password is set, HTTP Basic Auth will be used. You may use an array to specify more than one user. username: '', // Username for HTTP Basic Auth diff --git a/lib/manager.js b/lib/manager.js index b7edca7..132727b 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -5,9 +5,10 @@ var Server = require('./server') var filePath = 'servers.json' -var Manager = function (config, logs) { +var Manager = function (config, logs, mods) { this.config = config this.logs = logs + this.mods = mods this.serversArr = [] this.serversHash = {} } @@ -41,7 +42,7 @@ Manager.prototype.removeServer = function (id) { } Manager.prototype._addServer = function (data) { - var server = new Server(this.config, this.logs, data) + var server = new Server(this.config, this.logs, this.mods, data) this.serversArr.push(server) this.serversArr.sort(function (a, b) { return a.title.localeCompare(b.title) diff --git a/lib/mods.js b/lib/mods.js index 3ee34a2..0395a3d 100644 --- a/lib/mods.js +++ b/lib/mods.js @@ -21,6 +21,12 @@ Mods.prototype.delete = function (mod, cb) { }) } +Mods.prototype.find = function (id) { + this.mods.find(function (mod) { + return mod.id === id + }) +} + Mods.prototype.updateMods = function () { var self = this glob('**/{@*,csla,gm,vn}/addons', { cwd: self.config.path }, function (err, files) { @@ -28,9 +34,11 @@ Mods.prototype.updateMods = function () { console.log(err) } else { var mods = files.map(function (file) { + var name = path.join(file, '..') return { // Find actual parent mod folder from addons folder - name: path.join(file, '..') + id: name, + name: name } }) diff --git a/lib/server.js b/lib/server.js index 7c785a0..24cb90a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -17,9 +17,10 @@ var queryTypes = { ofpresistance: 'operationflashpoint' } -var Server = function (config, logs, options) { +var Server = function (config, logs, mods, options) { this.config = config this.logs = logs + this.modsManager = mods this.update(options) } @@ -95,6 +96,21 @@ Server.prototype.queryStatus = function () { ) } +Server.prototype.getMods = function () { + var self = this + return this.mods.map(function (mod) { + return self.modsManager.find(mod) + }).filter(function (mod) { + return mod + }).map(function (mod) { + if (self.config.type === 'linux' && self.config.steam && self.config.steam.path) { + return mod.path.replace(self.config.steam.path, 'workshop/') + } + + return mod.path + }) +} + Server.prototype.getParameters = function () { var parameters = [] @@ -132,6 +148,7 @@ Server.prototype.start = function () { return this } + var mods = this.getMods() var parameters = this.getParameters() var server = new ArmaServer.Server({ additionalConfigurationOptions: this.getAdditionalConfigurationOptions(), @@ -147,7 +164,7 @@ Server.prototype.start = function () { hostname: this.createServerTitle(this.title), localClient: this.number_of_headless_clients > 0 ? ['127.0.0.1'] : null, missions: this.missions, - mods: this.mods, + mods: mods, motd: (this.motd && this.motd.split('\n')) || null, parameters: parameters, password: this.password, @@ -197,6 +214,7 @@ Server.prototype.startHeadlessClientsIfNeeded = function () { } Server.prototype.startHeadlessClients = function () { + var mods = this.getMods() var parameters = this.getParameters() var self = this var headlessClientInstances = _.times(this.number_of_headless_clients, function (i) { @@ -204,7 +222,7 @@ Server.prototype.startHeadlessClients = function () { filePatching: self.file_patching, game: self.config.game, host: '127.0.0.1', - mods: self.mods, + mods: mods, parameters: parameters, password: self.password, path: self.config.path, diff --git a/lib/steam_mods.js b/lib/steam_mods.js new file mode 100644 index 0000000..b30ee14 --- /dev/null +++ b/lib/steam_mods.js @@ -0,0 +1,96 @@ +var events = require('events') +var ArmaSteamWorkshop = require('arma-steam-workshop') + +var SteamMods = function (config) { + this.config = config + this.armaSteamWorkshop = new ArmaSteamWorkshop(this.config.steam) + this.mods = [] +} + +SteamMods.prototype = new events.EventEmitter() + +SteamMods.prototype.delete = function (mod, cb) { + var self = this + this.armaSteamWorkshop.deleteMod(mod, function (err) { + if (err) { + console.log(err) + } else { + self.updateMods() + } + + if (cb) { + cb(err) + } + }) +} + +SteamMods.prototype.find = function (id) { + return this.mods.find(function (mod) { + return mod.id === id + }) +} + +SteamMods.prototype.download = function (workshopId, cb) { + var self = this + this.armaSteamWorkshop.downloadMod(workshopId, function (err) { + self.addStatusForCurrentDowloads() + self.updateMods() + + if (cb) { + cb(err) + } + }) + self.addStatusForCurrentDowloads() + self.emit('mods', this.mods) +} + +SteamMods.prototype.search = function (query, cb) { + this.armaSteamWorkshop.search(query, cb) +} + +SteamMods.prototype.updateMods = function () { + var self = this + this.armaSteamWorkshop.mods(function (err, mods) { + if (err) { + console.log(err) + } else { + self.mods = mods + self.addStatusForCurrentDowloads() + self.emit('mods', mods) + } + }) +} + +SteamMods.prototype.isModDownloading = function (workshopId) { + return this.armaSteamWorkshop.currentDownloads[workshopId] === true +} + +SteamMods.prototype.addStatusForCurrentDowloads = function () { + var self = this + this.mods.forEach(function (mod) { + mod.downloading = self.isModDownloading(mod.id) + }) + + this.addDummyModsForCurrentDowloads() + this.emit('mods', this.mods) +} + +SteamMods.prototype.addDummyModsForCurrentDowloads = function () { + var self = this + for (var workshopId in this.armaSteamWorkshop.currentDownloads) { + var mod = self.mods.find(function (mod) { + return mod.id === workshopId + }) + + if (!mod) { + self.mods.splice(0, 0, { + id: workshopId, + name: workshopId, + path: null, + downloading: true + }) + } + } +} + +module.exports = SteamMods diff --git a/package.json b/package.json index f4f294b..74c8d1b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "arma-server": "0.0.10", + "arma-steam-workshop": "https://github.com/Dahlgren/node-arma-steam-workshop/tarball/0396b9941eb43f714bd11df2bc5a99f0b283313e", "async": "^0.9.0", "backbone": "1.3.3", "backbone.bootstrap-modal": "https://github.com/powmedia/backbone.bootstrap-modal/archive/632210077c2424be2ee6ea2aafe0d3fe016ae524.tar.gz", diff --git a/public/js/app/models/mod.js b/public/js/app/models/mod.js index f5a278f..ea3b05c 100644 --- a/public/js/app/models/mod.js +++ b/public/js/app/models/mod.js @@ -2,8 +2,11 @@ var Backbone = require('backbone') module.exports = Backbone.Model.extend({ defaults: { - name: '' + id: '', + downloading: false, + name: '', + path: '' }, - idAttribute: 'name', + idAttribute: 'id', urlRoot: '/api/mods/' }) diff --git a/public/js/app/views/mods/download/form.js b/public/js/app/views/mods/download/form.js new file mode 100644 index 0000000..05d1244 --- /dev/null +++ b/public/js/app/views/mods/download/form.js @@ -0,0 +1,82 @@ +var $ = require('jquery') +var _ = require('underscore') +var Marionette = require('marionette') +var Ladda = require('ladda') +var Mods = require('app/collections/mods') +var tpl = require('tpl/mods/download/form.html') +var sweetAlert = require('sweet-alert') + +module.exports = Marionette.ItemView.extend({ + + events: { + submit: 'beforeSubmit' + }, + + template: _.template(tpl), + + initialize: function (options) { + this.mods = options.mods + this.collection = new Mods() + this.bind('ok', this.submit) + this.bind('shown', this.shown) + + var self = this + this.listenTo(this.mods, 'change reset add remove', function () { + self.collection.trigger('reset') + }) + }, + + beforeSubmit: function (e) { + e.preventDefault() + this.submit() + }, + + shown: function (modal) { + var $okBtn = modal.$el.find('.btn.ok') + $okBtn.addClass('ladda-button').attr('data-style', 'expand-left') + + this.laddaBtn = Ladda.create($okBtn.get(0)) + + this.$el.find('form .query').focus() + }, + + submit: function (modal) { + var self = this + var $form = this.$el.find('form') + + if (modal) { + self.modal.preventClose() + } + + $form.find('.form-group').removeClass('has-error') + $form.find('.help-block').text('') + + this.laddaBtn.start() + self.modal.$el.find('.btn.cancel').addClass('disabled') + + $.ajax({ + url: '/api/mods/', + type: 'POST', + data: { + id: $form.find('.query').val() + }, + dataType: 'json', + success: function (resp) { + self.laddaBtn.stop() + self.modal.$el.find('.ladda-button').removeClass('disabled') + self.modal.close() + }, + error: function (resp) { + self.laddaBtn.stop() + self.modal.$el.find('.ladda-button').removeClass('disabled') + self.modal.close() + + sweetAlert({ + title: 'Error', + text: 'An error occurred, please consult the logs', + type: 'error' + }) + } + }) + } +}) diff --git a/public/js/app/views/mods/list.js b/public/js/app/views/mods/list.js index a752ce3..91549c9 100644 --- a/public/js/app/views/mods/list.js +++ b/public/js/app/views/mods/list.js @@ -1,8 +1,11 @@ var $ = require('jquery') var _ = require('underscore') var Marionette = require('marionette') +var BootstrapModal = require('backbone.bootstrap-modal') var ListItemView = require('app/views/mods/list_item') +var DownloadFormView = require('app/views/mods/download/form') +var SearchFormView = require('app/views/mods/search/form') var tpl = require('tpl/mods/list.html') var template = _.template(tpl) @@ -13,7 +16,22 @@ module.exports = Marionette.CompositeView.extend({ template: template, events: { - 'click #refresh': 'refresh' + 'click #download': 'download', + 'click #refresh': 'refresh', + 'click #search': 'search' + }, + + download: function (event) { + event.preventDefault() + var view = new DownloadFormView({ mods: this.collection }) + var modal = new BootstrapModal({ + content: view, + animate: true, + cancelText: 'Close', + okText: 'Download' + }) + view.modal = modal + modal.open() }, refresh: function (event) { @@ -28,5 +46,18 @@ module.exports = Marionette.CompositeView.extend({ } }) + }, + + search: function (event) { + event.preventDefault() + var view = new SearchFormView({ mods: this.collection }) + var modal = new BootstrapModal({ + content: view, + animate: true, + cancelText: 'Close', + okText: 'Search' + }) + view.modal = modal + modal.open() } }) diff --git a/public/js/app/views/mods/list_item.js b/public/js/app/views/mods/list_item.js index 724e3a5..70de938 100644 --- a/public/js/app/views/mods/list_item.js +++ b/public/js/app/views/mods/list_item.js @@ -14,6 +14,10 @@ module.exports = Marionette.ItemView.extend({ 'click .destroy': 'deleteMod' }, + modelEvents: { + change: 'render' + }, + deleteMod: function (event) { var self = this sweetAlert({ diff --git a/public/js/app/views/mods/search/form.js b/public/js/app/views/mods/search/form.js new file mode 100644 index 0000000..9708f82 --- /dev/null +++ b/public/js/app/views/mods/search/form.js @@ -0,0 +1,90 @@ +var $ = require('jquery') +var _ = require('underscore') +var Marionette = require('marionette') +var ListItemView = require('app/views/mods/search/list_item') +var Ladda = require('ladda') +var Mods = require('app/collections/mods') +var tpl = require('tpl/mods/search/form.html') +var sweetAlert = require('sweet-alert') + +module.exports = Marionette.CompositeView.extend({ + + events: { + submit: 'beforeSubmit' + }, + + childView: ListItemView, + childViewContainer: 'tbody', + template: _.template(tpl), + + initialize: function (options) { + this.mods = options.mods + this.collection = new Mods() + this.bind('ok', this.submit) + this.bind('shown', this.shown) + + var self = this + this.listenTo(this.mods, 'change reset add remove', function () { + self.collection.trigger('reset') + }) + }, + + childViewOptions: function (options) { + options.set('mods', this.mods) + }, + + beforeSubmit: function (e) { + e.preventDefault() + this.submit() + }, + + shown: function (modal) { + var $okBtn = modal.$el.find('.btn.ok') + $okBtn.addClass('ladda-button').attr('data-style', 'expand-left') + + this.laddaBtn = Ladda.create($okBtn.get(0)) + + this.$el.find('form .query').focus() + }, + + submit: function (modal) { + var self = this + var $form = this.$el.find('form') + + if (modal) { + self.modal.preventClose() + } + + $form.find('.form-group').removeClass('has-error') + $form.find('.help-block').text('') + + this.laddaBtn.start() + self.modal.$el.find('.btn.cancel').addClass('disabled') + + $.ajax({ + url: '/api/mods/search', + type: 'POST', + data: { + query: $form.find('.query').val() + }, + dataType: 'json', + success: function (data) { + self.laddaBtn.stop() + self.modal.$el.find('.btn.cancel').removeClass('disabled') + self.collection.set(data) + }, + error: function () { + self.laddaBtn.stop() + $form.find('.form-group').addClass('has-error') + $form.find('.help-block').text('Problem searching, try again') + self.modal.$el.find('.btn.cancel').removeClass('disabled') + + sweetAlert({ + title: 'Error', + text: 'An error occurred, please consult the logs', + type: 'error' + }) + } + }) + } +}) diff --git a/public/js/app/views/mods/search/list_item.js b/public/js/app/views/mods/search/list_item.js new file mode 100644 index 0000000..4bdcdaf --- /dev/null +++ b/public/js/app/views/mods/search/list_item.js @@ -0,0 +1,52 @@ +var $ = require('jquery') +var _ = require('underscore') +var Marionette = require('marionette') +var Ladda = require('ladda') +var tpl = require('tpl/mods/search/list_item.html') + +var template = _.template(tpl) + +module.exports = Marionette.ItemView.extend({ + tagName: 'tr', + template: template, + + events: { + 'click .install': 'install' + }, + + templateHelpers: { + downloading: function () { + if (this.mods.get(this.id)) { + return this.mods.get(this.id).get('downloading') + } + + return false + } + }, + + install: function (event) { + var self = this + event.preventDefault() + + this.laddaBtn = Ladda.create(this.$el.find('.ladda-button').get(0)) + this.laddaBtn.start() + this.$el.find('.ladda-button').addClass('disabled') + + $.ajax({ + url: '/api/mods/', + type: 'POST', + data: { + id: this.model.get('id') + }, + dataType: 'json', + success: function (resp) { + self.laddaBtn.stop() + self.$el.find('.ladda-button').removeClass('disabled') + }, + error: function (resp) { + self.laddaBtn.stop() + self.$el.find('.ladda-button').removeClass('disabled') + } + }) + } +}) diff --git a/public/js/app/views/servers/info.js b/public/js/app/views/servers/info.js index fb5de69..b6169c0 100644 --- a/public/js/app/views/servers/info.js +++ b/public/js/app/views/servers/info.js @@ -57,5 +57,16 @@ module.exports = Marionette.LayoutView.extend({ self.render() }) }) + }, + + templateHelpers: function () { + var self = this + return { + mods: self.options.mods.filter(function (mod) { + return self.model.get('mods').indexOf(mod.get('id')) >= 0 + }).map(function (mod) { + return mod.get('name') + }) + } } }) diff --git a/public/js/app/views/servers/mods/list_item.js b/public/js/app/views/servers/mods/list_item.js index 3168672..923be89 100644 --- a/public/js/app/views/servers/mods/list_item.js +++ b/public/js/app/views/servers/mods/list_item.js @@ -11,7 +11,7 @@ module.exports = ModListItemView.extend({ templateHelpers: function () { return { - enabled: this.options.server.get('mods').indexOf(this.model.get('name')) > -1 + enabled: this.options.server.get('mods').indexOf(this.model.get('id')) > -1 } } }) diff --git a/public/js/app/views/servers/view.js b/public/js/app/views/servers/view.js index d93123a..1e6dc51 100644 --- a/public/js/app/views/servers/view.js +++ b/public/js/app/views/servers/view.js @@ -39,7 +39,7 @@ module.exports = Marionette.LayoutView.extend({ }, onRender: function () { - this.infoView.show(new InfoView({ model: this.model })) + this.infoView.show(new InfoView({ model: this.model, mods: this.mods })) this.missionsView.show(new MissionsView({ missions: this.missions, model: this.model })) this.modsView.show(new ModsListView({ collection: this.mods, server: this.model })) this.parametersView.show(new ParametersListView({ model: this.model })) diff --git a/public/js/tpl/mods/download/form.html b/public/js/tpl/mods/download/form.html new file mode 100644 index 0000000..e763533 --- /dev/null +++ b/public/js/tpl/mods/download/form.html @@ -0,0 +1,11 @@ +

+ Install mod from Steam Workshop by id +

+ +
+
+ + + +
+
diff --git a/public/js/tpl/mods/list.html b/public/js/tpl/mods/list.html index bc65135..ca874cc 100644 --- a/public/js/tpl/mods/list.html +++ b/public/js/tpl/mods/list.html @@ -3,6 +3,16 @@ Refresh + + + Download specific mod + + + + + Search & Download + + diff --git a/public/js/tpl/mods/list_item.html b/public/js/tpl/mods/list_item.html index 4b5ab12..7f3bf8b 100644 --- a/public/js/tpl/mods/list_item.html +++ b/public/js/tpl/mods/list_item.html @@ -2,10 +2,16 @@ <%-name%> diff --git a/public/js/tpl/mods/search/form.html b/public/js/tpl/mods/search/form.html new file mode 100644 index 0000000..79ab0a5 --- /dev/null +++ b/public/js/tpl/mods/search/form.html @@ -0,0 +1,23 @@ +

+ Search mods on Steam Workshop +

+ + +
+ + + +
+ + +
- - - Delete - + <% if (downloading) { %> +
+
+
+ <% } else { %> + + + Delete + -
+
+ <% } %>
+ + + + + + + + + +
Mod
diff --git a/public/js/tpl/mods/search/list_item.html b/public/js/tpl/mods/search/list_item.html new file mode 100644 index 0000000..ba3ead9 --- /dev/null +++ b/public/js/tpl/mods/search/list_item.html @@ -0,0 +1,27 @@ + +

+ <%-title%> +

+

+ <%-description%> +

+ + +

+ <% if (downloading()) { %> +

+
+
+ <% } else { %> + + + Install + + <% } %> +
+

+

+ <%-fileSizeFormatted%> +

+

+ diff --git a/public/js/tpl/servers/mods/list_item.html b/public/js/tpl/servers/mods/list_item.html index 9c28210..4d20e17 100644 --- a/public/js/tpl/servers/mods/list_item.html +++ b/public/js/tpl/servers/mods/list_item.html @@ -1,7 +1,7 @@
diff --git a/routes/mods.js b/routes/mods.js index ebc47aa..9a11063 100644 --- a/routes/mods.js +++ b/routes/mods.js @@ -7,6 +7,16 @@ module.exports = function (modsManager) { res.send(modsManager.mods) }) + router.post('/', function (req, res) { + modsManager.download(req.body.id) + res.status(204).send() + }) + + router.put('/:mod', function (req, res) { + modsManager.download(req.params.mod) + res.status(204).send() + }) + router.delete('/:mod', function (req, res) { modsManager.delete(req.params.mod, function (err) { if (err) { @@ -22,5 +32,16 @@ module.exports = function (modsManager) { res.status(204).send() }) + router.post('/search', function (req, res) { + var query = req.body.query || '' + modsManager.search(query, function (err, mods) { + if (err || !mods) { + res.status(500).send(err) + } else { + res.send(mods) + } + }) + }) + return router } diff --git a/test/lib/server.js b/test/lib/server.js index 3dfa552..9ab3b7b 100644 --- a/test/lib/server.js +++ b/test/lib/server.js @@ -5,14 +5,14 @@ var Server = require('../../lib/server.js') describe('Server', function () { describe('generateId()', function () { it('should include title', function () { - var server = new Server(null, null, { title: 'title.with.lot.of.dots' }) + var server = new Server(null, null, null, { title: 'title.with.lot.of.dots' }) server.generateId().should.eql('title-with-lot-of-dots') }) }) describe('toJSON()', function () { it('should include title', function () { - var server = new Server(null, null, { title: 'test' }) + var server = new Server(null, null, null, { title: 'test' }) server.toJSON().should.have.property('title', 'test') }) }) diff --git a/test/lib/steam_mods.js b/test/lib/steam_mods.js new file mode 100644 index 0000000..0277fc7 --- /dev/null +++ b/test/lib/steam_mods.js @@ -0,0 +1,21 @@ +require('should') + +var SteamMods = require('../../lib/steam_mods.js') + +var dummyMods = [ + { + id: 'test', + name: 'test' + } +] + +describe('SteamMods', function () { + describe('find()', function () { + it('should find mod', function () { + var steamMods = new SteamMods({}) + steamMods.mods = dummyMods + var mod = steamMods.find('test') + mod.should.eql(dummyMods[0]) + }) + }) +})