Result depends on it
function Adder(first, second) {
this.first = first;
this.second = second;
this.sum = function () {
return this.first + this.second;
}
}
var adder = new Adder(2, 3);
adder.sum();
Introduces an additional parameter: TIME
function Adder(first, second) {
...
this.setFirst = function(first) {
this.first = first;
}
this.setSecond = function(second) {
this.second = second;
}
}
var adder = new Adder(2, 3);
... //black hole :P
adder.sum(); // =?function badGuy(param) {
param.first = 10;
param.second = -1;
}
var adder = new Adder(2, 3);
badGuy(adder) //i don't know what bad guy does
adder.sum(); // =5, right? :Pdon't mutate the parameters inside a function (seriously, don't!)
function Adder() {
this.sum = function(first, second) {
return first + second;
}
}
var adder = new Adder();
adder.sum(2, 3); // =5, always!no state
depend only on parameters
play very nice with immutable objects
vm.toggleOptions = {
nextAvailableDate: nextDate,
label: "today",
today: moment.utc().startOf('day').toDate()
};
function toggleStartDateValue() {
vm.toggleOptions.toggleCheck = (moment.utc().startOf('day').isSame(vm.value));
vm.toggleOptions.label = (vm.toggleOptions.toggleCheck) ?
"today" :
"next available date";
vm.value = (vm.toggleOptions.toggleCheck) ?
vm.toggleOptions.nextAvailableDate :
vm.toggleOptions.today;
}vm.toggleOptions = {
nextAvailableDate: nextDate,
label: "today",
today: moment.utc().startOf('day').toDate()
};
function toggleStartDateValue() {
# check if vm.value is today
vm.toggleOptions.toggleCheck = moment(vm.toggleOptions.today).utc().isSame(vm.value);
# check if the date picker reads today and vm.value was manually set to today
vm.toggleOptions.datepickerToday = vm.toggleOptions.label === "today" &&
vm.toggleOptions.toggleCheck;
if (vm.toggleOptions.datepickerToday) {
vm.toggleOptions.label = "next available date";
vm.value = vm.toggleOptions.today;
} else {
vm.toggleOptions.label = (vm.toggleOptions.toggleCheck) ?
"today" :
"next available date";
vm.value = (vm.toggleOptions.toggleCheck) ?
vm.toggleOptions.nextAvailableDate :
vm.toggleOptions.today;
}
}state is vm.value... + the label => complexity!
what is the public interface?
should only be the label, but a lot is exposed
vm.toggleStartDateValue = toggleStartDateValue;
vm.toggleLabel = toggleLabel;
var nextAvailableDate = nextDate;
function toggleStartDateValue() {
vm.value = isToday() ? nextAvailableDate : today().toDate();
}
function toggleLabel() {
return isToday() ? 'next available date' : 'today';
}
function isToday() {
return (today().isSame(vm.value));
}
function today() {
return moment.utc().startOf('day');
}function formatStats(stats) {
stats.chart = {
labels: ['<2 weeks', '<4 weeks'],
colors: ['#cc3a3a', '#f4aa16'],
data: [stats.global.lessThanTwoWeeks, stats.global.lessThanFourWeeks],
options: {responsive: true, maintainAspectRatio: true}
};
stats.byLocation = R.reduce(function (acc, locationInfo) {
return R.merge(acc, R.objOf(locationInfo.location.code, locationInfo));
}, {}, stats.byLocation);
return stats;
}describe('directive', function () {
var stats;
beforeEach(function () {
stats = formatStats(insuranceReportStats);
...
element = compile('<div insurance-report-stats="stats"></div>')(scope);
scope.$digest();
});
it('test something', function () {...});
it('test something else', function () {...});
it('test something another thing', function () {...});
});open the door
go to the third room on the left
turn right
open the fridge door
look on the second shelf
move the milk bottle
take one of the beer cans
enjoy!
ring the door bell
ask for a beer
wait
here you go, enjoy!
function FleetStatusStats() {
var directive = {
scope: {},
bindToController: {
stats: '=fleetStatusStats',
},
controllerAs: 'vm',
controller: FleetStatusStatsCtrl
};
return directive;
}<div ...>
{{ vm.stats.global.total }}
</div>
<div ...>
{{ vm.stats.chartRevenue.data }}
</div>
<li ng-repeat="chunk in vm.stats.byLocation.chunks">
<div ...>
{{ chunk[0].location.stats.fullyMissionCapable }}
</div>
</li>it('should have correct values', function () {
var text = element.text();
expect(text).toContain('Active Aircraft');
expect(text).toContain('N-Rev');
expect(text).toContain('Rev');
expect(text).toContain('Bagram');
expect(text).toContain('Malta');
expect(text).toContain('3 FMC');
expect(text).toContain('2 FMC');
expect(text).toContain('4 NMC');
expect(text).toContain('1 NMC');
});function FleetStatusStats() {
var directive = {
scope: {},
bindToController: {
stats: '=fleetStatusStats',
},
controllerAs: 'vm',
controller: FleetStatusStatsCtrl
};
return directive;
}
function FleetStatusStatsCtrl() {
function locationName(code) {
return vm.stats.byLocation[code].location.name;
}
function statsForLocation(code) {
return vm.stats.byLocation[code].stats;
}
...
}<div ...>
{{ vm.globalStats().total }}
</div>
<div ...>
{{ vm.revenueChart() }}
</div>
<li ng-repeat="chunk in vm.chunks">
<div ...>
{{ vm.statsForLocation(chunk[0]).fullyMissionCapable }}
</div>
</li>it('should return the correct stats', function () {
expect(ctrl.statsForLocation('foo')).toEqual({...});
});
it('should have the correct chunks', function () {
expect(ctrl.chunks).toEqual([...]);
});function algHudInsuranceReportDetails() {
return {
restrict: 'A',
scope: {
stats: '=algHudInsuranceReportDetails'
}
};
}
function AlgHudInsuranceReportDetailsCtrl(stats) {
var vm = this;
vm.stats = stats;
vm.getInsuranceStatsByLocation = getInsuranceStatsByLocation;
function getInsuranceStatsByLocation() {
return vm.stats ? vm.stats.insuranceReportStats.byCountry.chunks : [];
}
}
accept only what you need
simplifies your code
simplifies the caller's life :D
simplifies refactorings
no need to navigate btw controller and view to see what data is used
simplifies testing (a lot!)
test the entire interface, not parts
function api(item) {
item.whatINeed = computeIt(item);
return item;
}
// tested like this:
it('...', function () {
var input = {
useful: ...,
blah: ...
};
var result = api(input);
expect(result.whatINeed).toEqual(...);
// what about result.useful, result.blah ?
// are they needed, how can api()
// be refactored?
});function api(item) {
return {
whatINeed: computeIt(item)
};
}
// tested like this:
it('...', function () {
var input = {
useful: ...,
blah: ...
};
expect(api(input)).toEqual(...);
});stubbing / mocking
creates a fake environment
isolation vs. real life
reusable service vs. implementation details
it('should return aog-report-new-bg for stats.newNMCs !=0', ...);
it('should return aog-report-discussed-bg for stats.discussedNMCs !== 0 && stats.newNMCs === 0',
...);
it('should return aog-report-none-bg for stats.discussedNMCs === 0 && stats.newNMCs === 0',
...);it('should return aog-report-new-bg if the location has new parts', ...);
it('should return aog-report-discussed-bg if the location has only discussed parts', ...);
it('should return aog-report-none-bg if the location has no parts', ...);var ... (long list of mocked stuff), stats, ...;
describe('directive', function () {
beforeEach(function () {
$scope = $rootScope.$new();
$scope.stats = { ... };
stats = $scope.stats;
};
it('test something', function () {...});
});
describe('controller', function () {
beforeEach(function() {
$scope = $rootScope.$new();
$scope.stats = stats;
});
it('test something but with no stats!', function () {
expect(ctrl).toBeDefined();
});
});Before
HTML would have nothing in it (sausage expressions fail silently)
After
test interface methods (fail)
beforeEach(function () {
$scope = $rootScope.$new();
$scope.stats = {
... }; // big fat object here :D
element = $compile('<div alg-hud-fleet-status="summary"></div>')($scope);
$scope.$digest();
});do I need it? is it generic?
extract reusable component/utility