diff --git a/cypress/integration/smoke-tests/feature-grid.spec.js b/cypress/integration/smoke-tests/feature-grid.spec.js
new file mode 100644
index 0000000000..6259acafc6
--- /dev/null
+++ b/cypress/integration/smoke-tests/feature-grid.spec.js
@@ -0,0 +1,135 @@
+///
+var os = require('../../support/selectors.js');
+
+describe('Feature grid', function() {
+ before('Login', function() {
+ cy.login();
+ });
+
+ it('Shows data', function() {
+ // Upload a file
+ cy.get(os.Toolbar.addData.OPEN_FILE_BUTTON).click();
+ cy.get(os.importDataDialog.DIALOG).should('be.visible');
+ cy.upload('smoke-tests/load-data-file-kml/test-features.kmz');
+ cy.get(os.importDataDialog.NEXT_BUTTON).click();
+ cy.get(os.importKMLDialog.DIALOG).should('be.visible');
+ cy.get(os.importKMLDialog.LAYER_TITLE_INPUT).should('be.visible');
+ cy.get(os.importKMLDialog.OK_BUTTON).click();
+
+ // Load a layer
+ cy.get(os.layersDialog.Tabs.Layers.Tree.LAYER_4)
+ .should('contain', 'smoke-tests/load-data-file-kml/test-features.kmz Features (291)');
+
+ cy.get(os.layersDialog.Tabs.Layers.Tree.LAYER_4).rightClick();
+ cy.get(os.layersDialog.Tabs.Layers.Tree.Type.featureLayer.Local.contextMenu.menuOptions.SHOW_FEATURES).click();
+ cy.get('[title=\'smoke-tests/load-data-file-kml/test-features.kmz\']').should('be.visible');
+
+ // Check status bar
+ cy.get('[data-testid=\'featurelist\']').within(function() {
+ cy.get('[ng-if=\'ctrl.status\']').should('be.visible');
+ cy.get('[ng-if=\'ctrl.status\']').should('contain', '291 records');
+ });
+
+ cy.get('[data-testid=\'featurelist\']').within(function() {
+ cy.get('.js-source-grid').should('be.visible');
+ cy.get('.js-source-grid').within(function() {
+ cy.get('.slick-header').should('be.visible');
+ // Check columns
+ cy.get('.slick-header').within(function() {
+ cy.get('.slick-column-name').should('have.length', 14);
+ cy.get('.slick-column-name:empty').should('exist');
+ cy.get('.slick-column-name').contains('TIME');
+ cy.get('.slick-column-name').contains('MGRS');
+ cy.get('.slick-column-name').contains('LAT_DMS');
+ cy.get('.slick-column-name').contains('LON_DMS');
+ cy.get('.slick-column-name').contains('LAT');
+ cy.get('.slick-column-name').contains('LON');
+ cy.get('.slick-column-name').contains('LAT_DDM');
+ cy.get('.slick-column-name').contains('LON_DDM');
+ cy.get('.slick-column-name').contains('visibility');
+ cy.get('.slick-column-name').contains('scan_start');
+ cy.get('.slick-column-name').contains('frp_mw');
+ cy.get('.slick-column-name').contains('confidence');
+ cy.get('.slick-column-name').contains('ID');
+ });
+ });
+
+ // Check data
+ cy.get('.slick-viewport').should('be.visible');
+ cy.get('.slick-viewport').within(function() {
+ cy.get('.slick-cell').contains('2018-12-18 17:08:37Z');
+ });
+ });
+
+ // Check selection
+ cy.get('[data-testid=\'featurelist\'] .slick-row').first().click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (1 selected)');
+
+ // Check context menu works.
+ cy.get('[data-testid=\'featurelist\'] .slick-row').first().rightClick();
+ cy.get('[title=\'Exports data to a file\']').should('be.visible');
+
+ cy.get('[title=\'Deselects all items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-row').first().click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (1 selected)');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-row').first().rightClick();
+ cy.get('[title=\'Inverts the selection\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (290 selected)');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Selects all items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (291 selected)');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Deselects all items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (1 selected)');
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Hides selected items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '290 records (1 hidden)');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Displays all items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (1 selected)');
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Hides unselected items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should(
+ 'contain', '1 record (1 selected, 290 hidden)');
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Displays all items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '291 records (1 selected)');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Removes selected items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '290 records');
+
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '290 records (1 selected)');
+ cy.get('[data-testid=\'featurelist\'] .slick-grid').rightClick();
+ cy.get('[title=\'Removes the unselected items\']').click();
+ cy.get('[data-testid=\'featurelist\'] [ng-if=\'ctrl.status\']').should('contain', '1 record (1 selected)');
+
+ // Check Close button works
+ cy.get('[data-testid=\'featurelist\']').within(function() {
+ cy.get('[ng-click=\'ctrl.close()\']').should('be.visible');
+ cy.get('[ng-click=\'ctrl.close()\']').click();
+ });
+ cy.get('[title=\'smoke-tests/load-data-file-kml/test-features.kmz\']').should('not.exist');
+
+ // Clean up
+ cy.get(os.layersDialog.Tabs.Layers.Tree.LAYER_4)
+ .should('contain', 'smoke-tests/load-data-file-kml/test-features.kmz Features (1)');
+ cy.get(os.layersDialog.Tabs.Layers.Tree.LAYER_4).click();
+ cy.get(os.layersDialog.Tabs.Layers.Tree.Type.featureLayer.REMOVE_LAYER_BUTTON_WILDCARD).click();
+ cy.get(os.layersDialog.DIALOG).should('not.contain', 'smoke-tests/load-data-file-kml/test-features.kmz Features');
+ cy.get(os.Application.PAGE).type('v');
+ });
+});
diff --git a/cypress/support/selectors.js b/cypress/support/selectors.js
index 6fbbd396df..f539374b24 100644
--- a/cypress/support/selectors.js
+++ b/cypress/support/selectors.js
@@ -1414,6 +1414,7 @@ exports.layersDialog = {
REMOVE: '[title=\'Removes the layer\']',
RENAME: '[title=\'Rename the layer\']',
SHOW_DESCRIPTION: '[title=\'Gives details about the layer\']',
+ SHOW_FEATURES: '[title=\'Displays features in the layer\']',
EXPORT: '[title=\'Exports data from this layer\']',
ADD_TO_TRACK: '[title=\'Adds selected features (or all features if none are selected) ' +
'to an existing track.\']',
diff --git a/views/windows/featurelist.html b/views/windows/featurelist.html
index caf90eabbd..3fd583857b 100644
--- a/views/windows/featurelist.html
+++ b/views/windows/featurelist.html
@@ -1,4 +1,4 @@
-