Modern jQuery: Refactoring and Testing the Way Forward
Opinions, choose your own adventure, YRMV, etc.
</meta>
$(document).ready(function () {
$('#fetch').click(function () {
var stockSymbol = $.trim($('#stock-symbol').val().toUpperCase());
$.ajax('some_long_url', {
beforeSend: function() {
$('#current-stock-price').html('Loading ...');
},
success: function (res) {
var quote = $(res).find('[symbol="' + stockSymbol + '"]');
var lastTradePrice = quote.find('LastTradePriceOnly').text();
$('#current-stock-price').html('' + stockSymbol + ': $' + lastTradePrice + ' retrieved at ' + new Date().toString());
$('#stock-price-log').html($('#stock-price-log').html() + '' + stockSymbol + ' $' + lastTradePrice + ' retrieved at ' + new Date().toString() + ' ');
}
});
});
});
View on GitHub
Maintainability, testability, warm fuzzies
External files, strict mode, IIFE
import { something } from 'somewhere';
Continue using jQuery
Or, switch to something else
fetch
, etc.)
Refactoring ahead
$.ajax(url).done(function (res) {
var quote = $(res).find('[symbol="' + stockSymbol + '"]');
var lastTradePrice = quote.find('LastTradePriceOnly').text();
// More code not related to getting the lastTradePrice
});
// data-provider.js
import $ from 'jquery';
export default class DataProvider {
getStockPrice(stockSymbol) {
if (!stockSymbol) {
throw new Error('Must provide a stockSymbol');
}
const url = 'http://query.yahooapis.com/v1/public/yql'
+ `?q=select * from yahoo.finance.quotes where symbol in ("${stockSymbol}")`
+ '&diagnostics=true'
+ '&env=http://datatables.org/alltables.env';
return fetch(url)
.then(res => res.text())
.then(text => {
const quote = $(text).find(`[symbol="${stockSymbol}"]`);
const lastTradePrice = quote.find('LastTradePriceOnly').text();
return lastTradePrice;
});
}
}
// Usage:
new DataProvider().getStockPrice('MSFT').then(lastTradePrice => {
/* Code here */
});
If you want to explore further refactoring
examine
start
and
finish.
Single obvious way to make it work.
import StockRetriever from './stock-retriever';
import DataProvider from './data-provider';
import UiProvider from './ui-provider';
new StockRetriever(new DataProvider(), new UiProvider())
.init();
Add tests while you refactor
import test from 'ava';
test('simple test', t => {
t.true(true);
});
import test from 'ava';
test('should return stock price', t => {
return new DataProvider()
.getStockPrice('TEST')
.then(lastTradePrice => {
t.is(lastTradePrice, '123.45');
});
});
import test from 'ava';
import fetchMock from 'fetch-mock';
import DataProvider from '../src/data-provider';
test.beforeEach(() => {
const response = `
123.45
`;
fetchMock.get('*', response);
});
test.afterEach(() => {
fetchMock.restore();
});
test('should return stock price', t => {
return new DataProvider()
.getStockPrice('TEST')
.then(lastTradePrice => {
t.is(lastTradePrice, '123.45');
});
});
// https://github.com/avajs/ava/blob/20ab39de046e527dec7b369f375d6c8e5fd4f5e1/docs/recipes/browser-testing.md#setup-jsdom
global.document = require('jsdom').jsdom('');
global.window = document.defaultView;
global.navigator = window.navigator;
You just learned a language agnostic skill!
Congratulations: You've reached the next level!