22 |
23 | All functions in an app:
24 |
25 |
26 |
27 | ## Development
28 |
29 | ### Setup
30 |
31 | #### 1) Install dependencies
32 |
33 | ```sh
34 | npm install
35 |
36 | # if you want webpack globally
37 | sudo npm install -g webpack@^1.14.0
38 | ```
39 |
40 | #### 2) Start Functions API (see [Fn on GitHub](http://github.com/fnproject/fn))
41 |
42 | ```sh
43 | fn start
44 | ```
45 |
46 | #### 3) Compile assets
47 |
48 | ```sh
49 | # option 1: if global webpack
50 | webpack
51 |
52 | # option 2: if local webpack
53 | npx webpack
54 | ```
55 |
56 | #### 4) Start web server
57 |
58 | ```sh
59 | PORT=4000 FN_API_URL=http://localhost:8080 npm start
60 | ```
61 |
62 | * `PORT` - port to run UI on. Optional, 4000 by default
63 | * `FN_API_URL` - Functions API URL. Required
64 |
65 | #### 5) View in browser
66 |
67 | [http://localhost:4000/](http://localhost:4000/)
68 |
69 | #### Configuring log levels
70 |
71 | UI uses [console-logging](https://www.npmjs.com/package/config-logger) for server-side logging.
72 | This supports log levels of `debug`, `verbose`, `info`, `warn` and `error`. By default the log level is `info` (this is configured in `config/default.json`). To set a log level of `debug`, use
73 |
74 | ```
75 | NODE_CONFIG='{"logLevel":"debug"}' PORT=4000 FN_API_URL=http://localhost:8080 npm start
76 |
77 | ```
78 |
79 | ### Automated Testing
80 | #### Integration tests
81 |
82 | The Fn UI has some basic Selenium integration tests that can be used to automatically test the UI's core functionality. These tests use the `mocha` testing framework as the driver.
83 |
84 | To run the tests:
85 |
86 | ##### 1) Install the Chrome Driver
87 | Download the ChromeDriver from [Google](https://sites.google.com/a/chromium.org/chromedriver/downloads) and put it in a place which is in your path e.g. `/usr/local/bin/chromedriver`.
88 |
89 | ##### 2) Install the Node dev dependencies
90 | Ensure you have the Node dev dependencies installed with:
91 | ```
92 | npm install [--only dev]
93 | ```
94 |
95 | ##### 3) Run the Fn interface
96 | See the instructions above for how to start the Node webserver.
97 |
98 | ##### 4) Configure your tests
99 | Edit [test_integration/etc/config.yaml](test_integration/etc/config.yaml) accordingly e.g. point `fn_url` to your Fn UI if you're not running it at its default location.
100 |
101 | ##### 5) Run the tests
102 | ```
103 | npm run test-integration
104 | ```
--------------------------------------------------------------------------------
/test_integration/lib/app_page_selector.js:
--------------------------------------------------------------------------------
1 | const ElementDetails = require('./element_details.js');
2 |
3 | /*
4 | * This module contains information about how to find elements on the Fn UI's
5 | * App Details page
6 | */
7 |
8 | // Return the CSS selector for the table row which contains information about
9 | // the specified Function
10 | function _fnTableRowSelector(fnName) {
11 | return `#fnTable tr[name="${fnName}"]`;
12 | }
13 |
14 | // Helper function for HTML elements which are located in the Functions table.
15 | // It appends the element's selector to the end of the Fn Table row selector
16 | function _fnTableRowElement(fnName, elementSelector) {
17 | return new ElementDetails(
18 | _fnTableRowSelector(fnName) + ' ' + elementSelector,
19 | ElementDetails.TYPE.CSS
20 | );
21 | }
22 |
23 | // Return information on how to find the Function's row in the Fn Table
24 | exports.fnTableRow = function(fnName) {
25 | return new ElementDetails(
26 | _fnTableRowSelector(fnName),
27 | ElementDetails.TYPE.CSS
28 | );
29 | };
30 |
31 | // Return information on how to find the openCreateFn button
32 | exports.openCreateFnBtn = function() {
33 | return new ElementDetails('openCreateFn', ElementDetails.TYPE.ID);
34 | };
35 |
36 | // Return information on how to find the Functions table
37 | exports.fnTable = function() {
38 | return new ElementDetails('fnTable', ElementDetails.TYPE.ID);
39 | };
40 |
41 | // Return information on how to find the create/edit Function submit button
42 | exports.submitFnBtn = function() {
43 | return new ElementDetails('submitFn', ElementDetails.TYPE.ID);
44 | };
45 |
46 | // Return information on how to find the fn name input field
47 | exports.fnNameInput = function() {
48 | return new ElementDetails('fnName', ElementDetails.TYPE.ID);
49 | };
50 |
51 | // Return information on how to find the fn image input field
52 | exports.fnImageInput = function() {
53 | return new ElementDetails('fnImage', ElementDetails.TYPE.ID);
54 | };
55 |
56 | // Return information on how to find the fn memory input field
57 | exports.fnMemoryInput = function() {
58 | return new ElementDetails('fnMemory', ElementDetails.TYPE.ID);
59 | };
60 |
61 | // Return information on how to find the fn timeout input field
62 | exports.fnTimeoutInput = function() {
63 | return new ElementDetails('fnTimeout', ElementDetails.TYPE.ID);
64 | };
65 |
66 | // Return information on how to find the fn idle timeout input field
67 | exports.fnIdleTimeoutInput = function() {
68 | return new ElementDetails('fnIdleTimeout', ElementDetails.TYPE.ID);
69 | };
70 |
71 | // Return information on how to find the edit Fn button
72 | exports.openEditFnBtn = function(fnName) {
73 | return _fnTableRowElement(fnName, '[name="openEditFn"]');
74 | };
75 |
76 | // Return information on how to find the Fn more options button
77 | exports.openMoreOptionsBtn = function(fnName) {
78 | return _fnTableRowElement(fnName, '[name="openMoreOptions"]');
79 | };
80 |
81 | // Return information on how to find the delete Fn button
82 | exports.deleteFnBtn = function(fnName) {
83 | return _fnTableRowElement(fnName, '[name="deleteFn"]');
84 | };
85 |
--------------------------------------------------------------------------------
/test_integration/test_app_page.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const randomstring = require('randomstring');
3 |
4 | const AppDetails = require('./lib/app_details.js');
5 | const AppPage = require('./lib/app_page.js');
6 | const Config = require('./lib/config.js');
7 | const FnDetails = require('./lib/fn_details.js');
8 | const HomePage = require('./lib/homepage.js');
9 |
10 | /*
11 | * This test uses the Mocha testing framework with Selenium to test
12 | * the functionality of the App Details page (/app/$appId)
13 | */
14 | (async function test_app_page() {
15 | let config = new Config();
16 | let fn_url = config.get('fn_url');
17 |
18 | // Use a random App name so it's less likely to conflict
19 | // with an app that already exists on the Fn server
20 | let appName = randomstring.generate({
21 | length: 30,
22 | charset: 'alphabetic'
23 | });
24 | let appDetails = new AppDetails(appName);
25 |
26 | let fnName = 'myFn';
27 | let fnImage = 'fndemouser/myFn';
28 |
29 | try {
30 |
31 | describe('Test Fn UI app page', async function() {
32 | this.timeout(50000);
33 |
34 | // Before running the tests, create an app that we can test against
35 | let appUrl;
36 | before(async () => {
37 | let homepage = new HomePage();
38 | await homepage.visit(fn_url);
39 | await homepage.createApp(appDetails);
40 | await homepage.visitApp(appName);
41 |
42 | appUrl = await homepage.getCurrentUrl();
43 | await homepage.quit();
44 | });
45 |
46 | // Clean up after the tests have completed
47 | after(async () => {
48 | let homepage = new HomePage();
49 | await homepage.visit(fn_url);
50 | await homepage.deleteApp(appName);
51 | await homepage.quit();
52 | });
53 |
54 | let appPage;
55 | beforeEach(async () => {
56 | appPage = new AppPage();
57 | await appPage.visit(appUrl);
58 | });
59 |
60 | afterEach(async () => {
61 | await appPage.quit();
62 | });
63 |
64 | it('can load interface', async () => {
65 | assert.ok(await appPage.loadedCorrectly());
66 | });
67 |
68 | it('can create a function', async () => {
69 | let fnDetails = new FnDetails(fnName, fnImage);
70 | await appPage.createFn(fnDetails);
71 | });
72 |
73 | it('can edit a function', async () => {
74 | let newFnImage = fnImage + '2';
75 | let fnDetails = new FnDetails(fnName, newFnImage);
76 | await appPage.editFn(fnDetails);
77 | assert.equal(await appPage.getFnImage(fnDetails.name), newFnImage);
78 | });
79 |
80 | it('should disallow large memory allocation', async () => {
81 | let fnDetails = new FnDetails(fnName, null, Number.MAX_SAFE_INTEGER);
82 | await appPage.editFn(fnDetails);
83 |
84 | let errorText = await appPage.getError();
85 | assert.ok(errorText.includes('out of range'));
86 | });
87 |
88 | it('can delete a function', async () => {
89 | await appPage.deleteFn(fnName);
90 | });
91 | });
92 | } catch (err) {
93 | // eslint-disable-next-line no-console
94 | console.error(new Error(err.message));
95 | }
96 | })();
97 |
--------------------------------------------------------------------------------
/client/components/FnRunFunction.vue:
--------------------------------------------------------------------------------
1 |
2 | No statistics found for App. Possible reasons for this are:
18 || Name | 28 |Actions | 29 | 30 | 31 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
33 | |
35 | 36 | 54 | | 55 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
58 | No Apps
59 | |
60 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
63 | Loading...
64 | |
65 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Name | 26 |Image | 27 |Memory | 28 |Timeout | 29 |Idle Timeout | 30 |Actions | 31 | 32 | 33 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{fn.name}} | 35 |{{fn.image}} | 36 |{{fn.memory}} MB | 37 |{{fn.timeout}} | 38 |{{fn.idle_timeout}} | 39 |40 | 61 | | 62 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
65 |
66 | No Functions
67 |
68 | |
69 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
72 | Loading...
73 | |
74 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||