User:Jeblad/Gadget development

From mediawiki.org

This page grew out of frustration over the present gadget development environment. Development of gadgets for the Wikimedia sites either pesters other users with lots of small updates, which often breaks the gadgets, or the developer must set up locally a very large and potential confusing development environment. Much of the development of gadgets does not require a full gadget testing environment. If it is necessary then it can be done at one of the deployment sites, for example the English beta wiki.

Minimum dev environment[edit]

As much as possible of the development should be done locally. Do not pester your coeditors unnecessary! Small iterations should create a new stable gadget, like in a SCRUM cycle. This is not possible without some tweaking of your development environment.

Greasemonkey[edit]

One solution is to use Greasemonkey to intercept requests to Wikipedia, or whichever Wikimedia site you want, and then load replacement scripts or extra scripts from localhost. This is the primary enabler for local development.

Greasemonkey can be used for full development of scripts, but the scripts created this way is not quite what we want. The environment has also some quirks that make them less portable, and the environment is also somewhat fragile.

It is very straightforward to set up Greasmonkey for Firefox. Go to the download page on the Add-Ons site and "Add to Firefox".

A user script can then be created for each project, pointing to a directory on our local machine, like the following

Loader script
// ==UserScript==
// @name        Test
// @namespace   https://no.wikipedia.org/
// @description A script for loading gadget under development
// @include     /^https?://[-a-z]+\.wikipedia\.org/wiki/.*$/
// @include     /^https?://[-a-z]+\.wikipedia\.org/w/index.php.*$/
// @version     1
// @grant       none
// ==/UserScript==

function append(tag, obj) {
  var element;
  if (obj.rel == 'stylesheet')
    element = document.createElement('link');
  else if (obj.rel == 'script')
    element = document.createElement('script');
  if (element) {
    
    if (obj.rel)
      element.rel = obj.rel;
    if (obj.type)
      element.type = obj.type;
    if (obj.href)
      element.href = obj.href;
    if (obj.src)
      element.src = obj.src;
    if (tag == 'head' || tag == 'body') {
      document.getElementsByTagName(tag)[0].appendChild(element);
    }
  }
}

append('head', {rel:'stylesheet', type:'text/css', href:'http://localhost/dev/Test/style.css'});
append('head', {rel:'script', type:'text/javascript', src:'http://localhost/dev/Test/script.js'});

At some point it will probably be necessary to tweak the include andd exclude rules in Greasemonkey.

Web server[edit]

It is necessary to have a local web server, as using local files creates a lot of trouble. Ideally the webserver should deliver HTTPS, but is does not create any big problems to allow mixed content. If this does create problems, then one solution is to set up a special profile for development work. Another solution is to add the necessary packages to HTTPS.

Any web server works, like Httpd, Nginx, or Lighttpd, as long as it can deliver a local file on localhost. For this specific use we don't need anything really fast, but we need some way to limit access as it will run on a local computer.

Example[edit]

As an example a directory /var/www/dev can be set aside as a semi-public place for development. In our previous example there is a directory Test with a style file style.css and a script file style.css. For testing purposes two small files like the following can be put in the directory.

style.css
h1 { color: blue !important; }
script.js
$('h1').attr('style', 'background-color:yellow');

Medium dev environment[edit]

To make a a somewhat more functional environment we can add a testing facility to the environment. Because we

QUnit[edit]

Loader script
// ==UserScript==
// @name        Test
// @namespace   https://no.wikipedia.org/
// @description A short script for some testing
// @include     /^https?://[-a-z]+\.wikipedia\.org/wiki/.*$/
// @include     /^https?://[-a-z]+\.wikipedia\.org/w/index.php.*$/
// @version     1
// @grant       none
// ==/UserScript==


$( function() {
  console.log('call 1');
  append('head', {rel:'stylesheet', type:'text/css', href:'//code.jquery.com/qunit/qunit-1.18.0.css'});
  mw.util.addPortletLink(
    'p-personal',
    '#',
    'Test here',
    't-article-test',
    'Runs a QUnit test set on this page',
    null,
    null
  );

  $('#t-article-test').click(function(){
    console.log('call 2');
    $('<div id="qunit-background"></div><div id="qunit-wrapper"><div id="qunit"></div><div id="qunit-fixture"></div></div>').appendTo('body');
    $('#qunit-background').attr('style','opacity:0.6;background-color:#ffffff; position:fixed; top:0; left:0; right:0; bottom:0;');
    $('#qunit-wrapper').attr('style','position:fixed; top:5em; left:5em; right: 5em; bottom: 5em;');
    jQuery.getScript('https://code.jquery.com/qunit/qunit-1.18.0.js', function() {
      append('body', {rel:'script', type:'text/javascript', src:'http://localhost/resources/Test/test.js'});
    });
  });
  
});

function append(tag, obj) {
  var element;
  if (obj.rel == 'stylesheet')
    element = document.createElement('link');
  else if (obj.rel == 'script')
    element = document.createElement('script');
  if (element) {
    
    if (obj.rel)
      element.rel = obj.rel;
    if (obj.type)
      element.type = obj.type;
    if (obj.href)
      element.href = obj.href;
    if (obj.src)
      element.src = obj.src;
    if (typeof obj.async != "undefined")
      element.async = obj.async;
    if (tag == 'head' || tag == 'body') {
      document.getElementsByTagName(tag)[0].appendChild(element);
    }
  }
}

append('head', {rel:'stylesheet', type:'text/css', href:'http://localhost/resources/Test/style.css'});
append('head', {rel:'script', type:'text/javascript', src:'http://localhost/resources/Test/script.js'});

Source map[edit]

CSS[edit]

Firebug[edit]

See also[edit]

Annoying stuff[edit]