Merge branch 'master' into open199
This commit is contained in:
@@ -32,6 +32,7 @@
|
|||||||
<li><a href="architecture/">Architecture Overview</a></li>
|
<li><a href="architecture/">Architecture Overview</a></li>
|
||||||
<li><a href="guide/">Developer Guide</a></li>
|
<li><a href="guide/">Developer Guide</a></li>
|
||||||
<li><a href="tutorials/">Tutorials</a></li>
|
<li><a href="tutorials/">Tutorials</a></li>
|
||||||
|
<li><a href="process/">Development Process</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
156
docs/src/process/index.md
Normal file
156
docs/src/process/index.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# Development Cycle
|
||||||
|
|
||||||
|
Development of Open MCT Web occurs on an iterative cycle of
|
||||||
|
sprints and releases.
|
||||||
|
|
||||||
|
* A _sprint_ is three weeks in duration, and represents a
|
||||||
|
set of improvements that can be completed and tested by the
|
||||||
|
development team. Software at the end of the sprint is
|
||||||
|
"semi-stable"; it will have undergone reduced testing and may carry
|
||||||
|
defects or usability issues of lower severity, particularly if
|
||||||
|
there are workarounds.
|
||||||
|
* A _release_ occurs every four sprints. Releases are stable, and
|
||||||
|
will have undergone full acceptance testing to ensure that the
|
||||||
|
software behaves correctly and usably.
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
The sprint process assumes the presence of a __project manager.__
|
||||||
|
The project manager is responsible for
|
||||||
|
making tactical decisions about what development work will be
|
||||||
|
performed, and for coordinating with stakeholders to arrive at
|
||||||
|
higher-level strategic decisions about desired functionality
|
||||||
|
and characteristics of the software, major external milestones,
|
||||||
|
and so forth.
|
||||||
|
|
||||||
|
In the absence of a dedicated project manager, this role may be rotated
|
||||||
|
among members of the development team on a per-sprint basis.
|
||||||
|
|
||||||
|
Responsibilities of the project manager including:
|
||||||
|
|
||||||
|
* Maintaining (with agreement of stakeholders) a "road map" of work
|
||||||
|
planned for future releases/sprints; this should be higher-level,
|
||||||
|
usually expressed as "themes",
|
||||||
|
with just enough specificity to gauge feasibility of plans,
|
||||||
|
relate work back to milestones, and identify longer-term
|
||||||
|
dependencies.
|
||||||
|
* Determining (with assistance from the rest of the team) which
|
||||||
|
issues to work on in a given sprint and how they shall be
|
||||||
|
assigned.
|
||||||
|
* Pre-planning subsequent sprints to ensure that all members of the
|
||||||
|
team always have a clear direction.
|
||||||
|
* Scheduling and/or ensuring adherence to
|
||||||
|
[process points](#process-points).
|
||||||
|
* Responding to changes within the sprint (shifting priorities,
|
||||||
|
new issues) and re-allocating work for the sprint as needed.
|
||||||
|
|
||||||
|
## Sprint Calendar
|
||||||
|
|
||||||
|
Certain [process points](#process-points) are regularly scheduled in
|
||||||
|
the sprint cycle.
|
||||||
|
|
||||||
|
### Sprints by Release
|
||||||
|
|
||||||
|
Allocation of work among sprints should be planned relative to release
|
||||||
|
goals and milestones. As a general guideline, higher-risk work (large
|
||||||
|
new features which may carry new defects, major refactoring, design
|
||||||
|
changes with uncertain effects on usability) should be allocated to
|
||||||
|
earlier sprints, allowing for time in later sprints to ensure stability.
|
||||||
|
|
||||||
|
| Sprint | Focus |
|
||||||
|
|:------:|:--------------------------------------------------------|
|
||||||
|
| __1__ | Prototyping, design, experimentation. |
|
||||||
|
| __2__ | New features, refinements, enhancements. |
|
||||||
|
| __3__ | Feature completion, low-risk enhancements, bug fixing. |
|
||||||
|
| __4__ | Stability & quality assurance. |
|
||||||
|
|
||||||
|
### Sprints 1-3
|
||||||
|
|
||||||
|
The first three sprints of a release are primarily centered around
|
||||||
|
development work, with regular acceptance testing in the third
|
||||||
|
week. During this third week, the top priority should be passing
|
||||||
|
acceptance testing (e.g. by resolving any blockers found); any
|
||||||
|
resources not needed for this effort should be used to begin work
|
||||||
|
for the subsequent sprint.
|
||||||
|
|
||||||
|
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||||
|
|:-----:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||||
|
| __1__ | Sprint plan | Tag-up | | | |
|
||||||
|
| __2__ | | Tag-up | | | Code freeze |
|
||||||
|
| __3__ | Sprint acceptance testing | Triage | | _Sprint acceptance testing*_ | Ship |
|
||||||
|
|
||||||
|
* If necessary.
|
||||||
|
|
||||||
|
### Sprint 4
|
||||||
|
|
||||||
|
The software must be stable at the end of the fourth sprint; because of
|
||||||
|
this, the fourth sprint is scheduled differently, with a heightened
|
||||||
|
emphasis on testing.
|
||||||
|
|
||||||
|
| Week | Mon | Tue | Wed | Thu | Fri |
|
||||||
|
|-------:|:-------------------------:|:------:|:---:|:----------------------------:|:-----------:|
|
||||||
|
| __1__ | Sprint plan | Tag-up | | | Code freeze |
|
||||||
|
| __2__ | Acceptance testing | Triage | | | |
|
||||||
|
| __3__ | _Acceptance testing*_ | Triage | | _Acceptance testing*_ | Ship |
|
||||||
|
|
||||||
|
* If necessary.
|
||||||
|
|
||||||
|
## Process Points
|
||||||
|
|
||||||
|
* __Sprint plan.__ Project manager allocates issues based on
|
||||||
|
theme(s) for sprint, then reviews with team. Each team member
|
||||||
|
should have roughly two weeks of work allocated (to allow time
|
||||||
|
in the third week for testing of work completed.)
|
||||||
|
* Project manager should also sketch out subsequent sprint so
|
||||||
|
that team may begin work for that sprint during the
|
||||||
|
third week, since testing and blocker resolution is unlikely
|
||||||
|
to require all available resources.
|
||||||
|
* __Tag-up.__ Check in and status update among development team.
|
||||||
|
May amend plan for sprint as-needed.
|
||||||
|
* __Code freeze.__ Any new work from this sprint
|
||||||
|
(features, bug fixes, enhancements) must be integrated by the
|
||||||
|
end of the second week of the sprint. After code freeze
|
||||||
|
(and until the end of the sprint) the only changes that should be
|
||||||
|
merged into the master branch should directly address issues
|
||||||
|
needed to pass acceptance testing.
|
||||||
|
* __Acceptance Testing.__ Structured testing with predefined
|
||||||
|
success criteria. No release should ship without passing
|
||||||
|
acceptance tests. Time is allocated in each sprint for subsequent
|
||||||
|
rounds of acceptance testing if issues are identified during a
|
||||||
|
prior round. Specific details of acceptance testing need to be
|
||||||
|
agreed-upon with relevant stakeholders and delivery recipients,
|
||||||
|
and should be flexible enough to allow changes to plans
|
||||||
|
(e.g. deferring delivery of some feature in order to ensure
|
||||||
|
stability of other features.) Baseline testing includes:
|
||||||
|
* __Testathon.__ Multi-user testing, involving as many users as
|
||||||
|
is feasible, plus development team. Open-ended; should verify
|
||||||
|
completed work from this sprint, test exploratorily for
|
||||||
|
regressions, et cetera.
|
||||||
|
* __24-Hour Test.__ A test to verify that the software remains
|
||||||
|
stable after running for longer durations. May include some
|
||||||
|
combination of automated testing and user verification (e.g.
|
||||||
|
checking to verify that software remains subjectively
|
||||||
|
responsive at conclusion of test.)
|
||||||
|
* __Automated Testing.__ Automated testing integrated into the
|
||||||
|
build. (These tests are verified to pass more often than once
|
||||||
|
per sprint, as they run before any merge to master, but still
|
||||||
|
play an important role in acceptance testing.)
|
||||||
|
* __Sprint Acceptance Testing.__ Subset of Acceptance Testing
|
||||||
|
which should be performed before shipping at the end of any
|
||||||
|
sprint. Time is allocated for a second round of
|
||||||
|
Sprint Acceptance Testing if the first round is not passed.
|
||||||
|
* __Triage.__ Team reviews issues from acceptance testing and uses
|
||||||
|
success criteria to determine whether or not they should block
|
||||||
|
release, then formulates a plan to address these issues before
|
||||||
|
the next round of acceptance testing. Focus here should be on
|
||||||
|
ensuring software passes that testing in order to ship on time;
|
||||||
|
may prefer to disable malfunctioning components and fix them
|
||||||
|
in a subsequent sprint, for example.
|
||||||
|
* __Ship.__ Tag a code snapshot that has passed acceptance
|
||||||
|
testing and deploy that version. (Only true if acceptance
|
||||||
|
testing has passed by this point; if acceptance testing has not
|
||||||
|
been passed, will need to make ad hoc decisions with stakeholders,
|
||||||
|
e.g. "extend the sprint" or "defer shipment until end of next
|
||||||
|
sprint.")
|
||||||
|
|
||||||
|
|
||||||
@@ -6,10 +6,10 @@
|
|||||||
</input>
|
</input>
|
||||||
<a class="ui-symbol icon icon-calendar"
|
<a class="ui-symbol icon icon-calendar"
|
||||||
ng-if="structure.format === 'utc' || !structure.format"
|
ng-if="structure.format === 'utc' || !structure.format"
|
||||||
ng-click="pickerActive = !pickerActive">
|
ng-click="picker.active = !picker.active">
|
||||||
</a>
|
</a>
|
||||||
<mct-popup ng-if="pickerActive">
|
<mct-popup ng-if="picker.active">
|
||||||
<div mct-click-elsewhere="pickerActive = false">
|
<div mct-click-elsewhere="picker.active = false">
|
||||||
<mct-control key="'datetime-picker'"
|
<mct-control key="'datetime-picker'"
|
||||||
ng-model="ngModel"
|
ng-model="ngModel"
|
||||||
field="field"
|
field="field"
|
||||||
|
|||||||
@@ -69,9 +69,12 @@ define(
|
|||||||
updateFromModel($scope.ngModel[$scope.field]);
|
updateFromModel($scope.ngModel[$scope.field]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.picker = { active: false };
|
||||||
|
|
||||||
$scope.$watch('structure.format', setFormat);
|
$scope.$watch('structure.format', setFormat);
|
||||||
$scope.$watch('ngModel[field]', updateFromModel);
|
$scope.$watch('ngModel[field]', updateFromModel);
|
||||||
$scope.$watch('textValue', updateFromView);
|
$scope.$watch('textValue', updateFromView);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateTimeFieldController;
|
return DateTimeFieldController;
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ define(
|
|||||||
position = [ rect.left, rect.top ],
|
position = [ rect.left, rect.top ],
|
||||||
popup = popupService.display(div, position);
|
popup = popupService.display(div, position);
|
||||||
|
|
||||||
|
// TODO: Handle in CSS;
|
||||||
|
// https://github.com/nasa/openmctweb/issues/298
|
||||||
|
div.css('z-index', 75);
|
||||||
|
|
||||||
transclude(function (clone) {
|
transclude(function (clone) {
|
||||||
div.append(clone);
|
div.append(clone);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ define(
|
|||||||
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
|
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exposes toggle state for date-time picker", function () {
|
||||||
|
expect(mockScope.picker.active).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
describe("when user input is invalid", function () {
|
describe("when user input is invalid", function () {
|
||||||
var newText, oldValue;
|
var newText, oldValue;
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "topic",
|
"key": "topic",
|
||||||
"implementation": "services/Topic.js"
|
"implementation": "services/Topic.js",
|
||||||
|
"depends": [ "$log" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "contextualize",
|
"key": "contextualize",
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var ERROR_PREFIX = "Error when notifying listener: ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `topic` service provides a way to create both named,
|
* The `topic` service provides a way to create both named,
|
||||||
* shared listeners and anonymous, private listeners.
|
* shared listeners and anonymous, private listeners.
|
||||||
@@ -46,7 +48,7 @@ define(
|
|||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
* @memberof platform/core
|
* @memberof platform/core
|
||||||
*/
|
*/
|
||||||
function Topic() {
|
function Topic($log) {
|
||||||
var topics = {};
|
var topics = {};
|
||||||
|
|
||||||
function createTopic() {
|
function createTopic() {
|
||||||
@@ -63,7 +65,11 @@ define(
|
|||||||
},
|
},
|
||||||
notify: function (message) {
|
notify: function (message) {
|
||||||
listeners.forEach(function (listener) {
|
listeners.forEach(function (listener) {
|
||||||
|
try {
|
||||||
listener(message);
|
listener(message);
|
||||||
|
} catch (e) {
|
||||||
|
$log.error(ERROR_PREFIX + e.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,13 +28,18 @@ define(
|
|||||||
|
|
||||||
describe("The 'topic' service", function () {
|
describe("The 'topic' service", function () {
|
||||||
var topic,
|
var topic,
|
||||||
|
mockLog,
|
||||||
testMessage,
|
testMessage,
|
||||||
mockCallback;
|
mockCallback;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testMessage = { someKey: "some value"};
|
testMessage = { someKey: "some value"};
|
||||||
|
mockLog = jasmine.createSpyObj(
|
||||||
|
'$log',
|
||||||
|
[ 'error', 'warn', 'info', 'debug' ]
|
||||||
|
);
|
||||||
mockCallback = jasmine.createSpy('callback');
|
mockCallback = jasmine.createSpy('callback');
|
||||||
topic = new Topic();
|
topic = new Topic(mockLog);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("notifies listeners on a topic", function () {
|
it("notifies listeners on a topic", function () {
|
||||||
@@ -65,6 +70,21 @@ define(
|
|||||||
expect(mockCallback).toHaveBeenCalledWith(testMessage);
|
expect(mockCallback).toHaveBeenCalledWith(testMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("is robust against errors thrown by listeners", function () {
|
||||||
|
var mockBadCallback = jasmine.createSpy("bad-callback"),
|
||||||
|
t = topic();
|
||||||
|
|
||||||
|
mockBadCallback.andCallFake(function () {
|
||||||
|
throw new Error("I'm afraid I can't do that.");
|
||||||
|
});
|
||||||
|
|
||||||
|
t.listen(mockBadCallback);
|
||||||
|
t.listen(mockCallback);
|
||||||
|
|
||||||
|
t.notify(testMessage);
|
||||||
|
expect(mockCallback).toHaveBeenCalledWith(testMessage);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ define(
|
|||||||
Math.floor(position.x / self.gridSize[0]),
|
Math.floor(position.x / self.gridSize[0]),
|
||||||
Math.floor(position.y / self.gridSize[1])
|
Math.floor(position.y / self.gridSize[1])
|
||||||
],
|
],
|
||||||
dimensions: DEFAULT_DIMENSIONS
|
dimensions: self.defaultDimensions()
|
||||||
};
|
};
|
||||||
// Mark change as persistable
|
// Mark change as persistable
|
||||||
if ($scope.commit) {
|
if ($scope.commit) {
|
||||||
|
|||||||
@@ -192,6 +192,29 @@ define(
|
|||||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
|
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
|
||||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
|
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("ensures a minimum frame size on drop", function () {
|
||||||
|
var style;
|
||||||
|
|
||||||
|
// Start with a very small frame size
|
||||||
|
testModel.layoutGrid = [ 1, 1 ];
|
||||||
|
mockScope.$watch.calls[0].args[1](testModel.layoutGrid);
|
||||||
|
|
||||||
|
// Notify that a drop occurred
|
||||||
|
testModel.composition.push('d');
|
||||||
|
mockScope.$on.mostRecentCall.args[1](
|
||||||
|
mockEvent,
|
||||||
|
'd',
|
||||||
|
{ x: 300, y: 100 }
|
||||||
|
);
|
||||||
|
mockScope.$watch.calls[0].args[1](['d']);
|
||||||
|
|
||||||
|
style = controller.getFrameStyle("d");
|
||||||
|
|
||||||
|
// Resulting size should still be reasonably large pixel-size
|
||||||
|
expect(parseInt(style.width, 10)).toBeGreaterThan(63);
|
||||||
|
expect(parseInt(style.height, 10)).toBeGreaterThan(31);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user