| { | ||
| "projectId": "4134ah" | ||
| } |
| { | ||
| "plugins": ["cypress"], | ||
| "env": { | ||
| "cypress/globals": true | ||
| } | ||
| } |
| const APP_URL = "http://localhost:3001"; | ||
| describe("Demo 3", () => { | ||
| it("is loaded with React properly", () => { | ||
| cy.visit(APP_URL); | ||
| cy.get("[data-test='list-header']").should("have.text", "My List"); | ||
| }); | ||
| it("should toggle visibility", () => { | ||
| cy.visit(APP_URL); | ||
| cy.get(".list-container").should("contain", "Block"); | ||
| cy.get("[data-test='toggle-visibility']").click(); | ||
| cy.get(".list-container").should("not.contain", "Block"); | ||
| }); | ||
| it("should add a block", () => { | ||
| cy.visit(APP_URL); | ||
| cy.get("[data-test='add-block']").click(); | ||
| cy.get(".list-container").should("contain", "3"); | ||
| }); | ||
| it("should be able to edit a newly added block", () => { | ||
| cy.visit(APP_URL); | ||
| cy.get("[data-test='add-block']").click(); | ||
| cy.get(".list-container").should("contain", "3"); | ||
| cy.get("[data-test='change-mode-2']").click(); | ||
| cy | ||
| .get("[data-test='input-2']") | ||
| .clear() | ||
| .type("Hello"); | ||
| cy.get("[data-test='save-2']").click(); | ||
| cy.get("[data-test='text-2']").should("contain", "Hello"); | ||
| cy.window().then(win => { | ||
| const snapshot = win.machine.getState(); | ||
| cy.visit(APP_URL); | ||
| cy.window().then(win => { | ||
| win.machine.setState(snapshot); | ||
| cy.get("[data-test='text-2']").should("contain", "Hello"); | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
| // // | ||
| // // **** Kitchen Sink Tests **** | ||
| // // | ||
| // // This app was developed to demonstrate | ||
| // // how to write tests in Cypress utilizing | ||
| // // all of the available commands | ||
| // // | ||
| // // Feel free to modify this spec in your | ||
| // // own application as a jumping off point | ||
| // // Please read our "Introduction to Cypress" | ||
| // // https://on.cypress.io/introduction-to-cypress | ||
| // describe('Kitchen Sink', function () { | ||
| // it('.should() - assert that <title> is correct', function () { | ||
| // // https://on.cypress.io/visit | ||
| // cy.visit('https://example.cypress.io') | ||
| // // Here we've made our first assertion using a '.should()' command. | ||
| // // An assertion is comprised of a chainer, subject, and optional value. | ||
| // // https://on.cypress.io/should | ||
| // // https://on.cypress.io/and | ||
| // // https://on.cypress.io/title | ||
| // cy.title().should('include', 'Kitchen Sink') | ||
| // // ↲ ↲ ↲ | ||
| // // subject chainer value | ||
| // }) | ||
| // context('Querying', function () { | ||
| // beforeEach(function () { | ||
| // // Visiting our app before each test removes any state build up from | ||
| // // previous tests. Visiting acts as if we closed a tab and opened a fresh one | ||
| // cy.visit('https://example.cypress.io/commands/querying') | ||
| // }) | ||
| // // Let's query for some DOM elements and make assertions | ||
| // // The most commonly used query is 'cy.get()', you can | ||
| // // think of this like the '$' in jQuery | ||
| // it('cy.get() - query DOM elements', function () { | ||
| // // https://on.cypress.io/get | ||
| // // Get DOM elements by id | ||
| // cy.get('#query-btn').should('contain', 'Button') | ||
| // // Get DOM elements by class | ||
| // cy.get('.query-btn').should('contain', 'Button') | ||
| // cy.get('#querying .well>button:first').should('contain', 'Button') | ||
| // // ↲ | ||
| // // Use CSS selectors just like jQuery | ||
| // }) | ||
| // it('cy.contains() - query DOM elements with matching content', function () { | ||
| // // https://on.cypress.io/contains | ||
| // cy.get('.query-list') | ||
| // .contains('bananas').should('have.class', 'third') | ||
| // // we can pass a regexp to `.contains()` | ||
| // cy.get('.query-list') | ||
| // .contains(/^b\w+/).should('have.class', 'third') | ||
| // cy.get('.query-list') | ||
| // .contains('apples').should('have.class', 'first') | ||
| // // passing a selector to contains will yield the selector containing the text | ||
| // cy.get('#querying') | ||
| // .contains('ul', 'oranges').should('have.class', 'query-list') | ||
| // // `.contains()` will favor input[type='submit'], | ||
| // // button, a, and label over deeper elements inside them | ||
| // // this will not yield the <span> inside the button, | ||
| // // but the <button> itself | ||
| // cy.get('.query-button') | ||
| // .contains('Save Form').should('have.class', 'btn') | ||
| // }) | ||
| // it('.within() - query DOM elements within a specific element', function () { | ||
| // // https://on.cypress.io/within | ||
| // cy.get('.query-form').within(function () { | ||
| // cy.get('input:first').should('have.attr', 'placeholder', 'Email') | ||
| // cy.get('input:last').should('have.attr', 'placeholder', 'Password') | ||
| // }) | ||
| // }) | ||
| // it('cy.root() - query the root DOM element', function () { | ||
| // // https://on.cypress.io/root | ||
| // // By default, root is the document | ||
| // cy.root().should('match', 'html') | ||
| // cy.get('.query-ul').within(function () { | ||
| // // In this within, the root is now the ul DOM element | ||
| // cy.root().should('have.class', 'query-ul') | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Traversal', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/traversal') | ||
| // }) | ||
| // // Let's query for some DOM elements and make assertions | ||
| // it('.children() - get child DOM elements', function () { | ||
| // // https://on.cypress.io/children | ||
| // cy.get('.traversal-breadcrumb').children('.active') | ||
| // .should('contain', 'Data') | ||
| // }) | ||
| // it('.closest() - get closest ancestor DOM element', function () { | ||
| // // https://on.cypress.io/closest | ||
| // cy.get('.traversal-badge').closest('ul') | ||
| // .should('have.class', 'list-group') | ||
| // }) | ||
| // it('.eq() - get a DOM element at a specific index', function () { | ||
| // // https://on.cypress.io/eq | ||
| // cy.get('.traversal-list>li').eq(1).should('contain', 'siamese') | ||
| // }) | ||
| // it('.filter() - get DOM elements that match the selector', function () { | ||
| // // https://on.cypress.io/filter | ||
| // cy.get('.traversal-nav>li').filter('.active').should('contain', 'About') | ||
| // }) | ||
| // it('.find() - get descendant DOM elements of the selector', function () { | ||
| // // https://on.cypress.io/find | ||
| // cy.get('.traversal-pagination').find('li').find('a') | ||
| // .should('have.length', 7) | ||
| // }) | ||
| // it('.first() - get first DOM element', function () { | ||
| // // https://on.cypress.io/first | ||
| // cy.get('.traversal-table td').first().should('contain', '1') | ||
| // }) | ||
| // it('.last() - get last DOM element', function () { | ||
| // // https://on.cypress.io/last | ||
| // cy.get('.traversal-buttons .btn').last().should('contain', 'Submit') | ||
| // }) | ||
| // it('.next() - get next sibling DOM element', function () { | ||
| // // https://on.cypress.io/next | ||
| // cy.get('.traversal-ul').contains('apples').next().should('contain', 'oranges') | ||
| // }) | ||
| // it('.nextAll() - get all next sibling DOM elements', function () { | ||
| // // https://on.cypress.io/nextall | ||
| // cy.get('.traversal-next-all').contains('oranges') | ||
| // .nextAll().should('have.length', 3) | ||
| // }) | ||
| // it('.nextUntil() - get next sibling DOM elements until next el', function () { | ||
| // // https://on.cypress.io/nextuntil | ||
| // cy.get('#veggies').nextUntil('#nuts').should('have.length', 3) | ||
| // }) | ||
| // it('.not() - remove DOM elements from set of DOM elements', function () { | ||
| // // https://on.cypress.io/not | ||
| // cy.get('.traversal-disabled .btn').not('[disabled]').should('not.contain', 'Disabled') | ||
| // }) | ||
| // it('.parent() - get parent DOM element from DOM elements', function () { | ||
| // // https://on.cypress.io/parent | ||
| // cy.get('.traversal-mark').parent().should('contain', 'Morbi leo risus') | ||
| // }) | ||
| // it('.parents() - get parent DOM elements from DOM elements', function () { | ||
| // // https://on.cypress.io/parents | ||
| // cy.get('.traversal-cite').parents().should('match', 'blockquote') | ||
| // }) | ||
| // it('.parentsUntil() - get parent DOM elements from DOM elements until el', function () { | ||
| // // https://on.cypress.io/parentsuntil | ||
| // cy.get('.clothes-nav').find('.active').parentsUntil('.clothes-nav') | ||
| // .should('have.length', 2) | ||
| // }) | ||
| // it('.prev() - get previous sibling DOM element', function () { | ||
| // // https://on.cypress.io/prev | ||
| // cy.get('.birds').find('.active').prev().should('contain', 'Lorikeets') | ||
| // }) | ||
| // it('.prevAll() - get all previous sibling DOM elements', function () { | ||
| // // https://on.cypress.io/prevAll | ||
| // cy.get('.fruits-list').find('.third').prevAll().should('have.length', 2) | ||
| // }) | ||
| // it('.prevUntil() - get all previous sibling DOM elements until el', function () { | ||
| // // https://on.cypress.io/prevUntil | ||
| // cy.get('.foods-list').find('#nuts').prevUntil('#veggies') | ||
| // }) | ||
| // it('.siblings() - get all sibling DOM elements', function () { | ||
| // // https://on.cypress.io/siblings | ||
| // cy.get('.traversal-pills .active').siblings().should('have.length', 2) | ||
| // }) | ||
| // }) | ||
| // context('Actions', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/actions') | ||
| // }) | ||
| // // Let's perform some actions on DOM elements | ||
| // // https://on.cypress.io/interacting-with-elements | ||
| // it('.type() - type into a DOM element', function () { | ||
| // // https://on.cypress.io/type | ||
| // cy.get('.action-email') | ||
| // .type('fake@email.com').should('have.value', 'fake@email.com') | ||
| // // .type() with special character sequences | ||
| // .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') | ||
| // .type('{del}{selectall}{backspace}') | ||
| // // .type() with key modifiers | ||
| // .type('{alt}{option}') //these are equivalent | ||
| // .type('{ctrl}{control}') //these are equivalent | ||
| // .type('{meta}{command}{cmd}') //these are equivalent | ||
| // .type('{shift}') | ||
| // // Delay each keypress by 0.1 sec | ||
| // .type('slow.typing@email.com', { delay: 100 }) | ||
| // .should('have.value', 'slow.typing@email.com') | ||
| // cy.get('.action-disabled') | ||
| // // Ignore error checking prior to type | ||
| // // like whether the input is visible or disabled | ||
| // .type('disabled error checking', { force: true }) | ||
| // .should('have.value', 'disabled error checking') | ||
| // }) | ||
| // it('.focus() - focus on a DOM element', function () { | ||
| // // https://on.cypress.io/focus | ||
| // cy.get('.action-focus').focus() | ||
| // .should('have.class', 'focus') | ||
| // .prev().should('have.attr', 'style', 'color: orange;') | ||
| // }) | ||
| // it('.blur() - blur off a DOM element', function () { | ||
| // // https://on.cypress.io/blur | ||
| // cy.get('.action-blur').type('I\'m about to blur').blur() | ||
| // .should('have.class', 'error') | ||
| // .prev().should('have.attr', 'style', 'color: red;') | ||
| // }) | ||
| // it('.clear() - clears an input or textarea element', function () { | ||
| // // https://on.cypress.io/clear | ||
| // cy.get('.action-clear').type('We are going to clear this text') | ||
| // .should('have.value', 'We are going to clear this text') | ||
| // .clear() | ||
| // .should('have.value', '') | ||
| // }) | ||
| // it('.submit() - submit a form', function () { | ||
| // // https://on.cypress.io/submit | ||
| // cy.get('.action-form') | ||
| // .find('[type="text"]').type('HALFOFF') | ||
| // cy.get('.action-form').submit() | ||
| // .next().should('contain', 'Your form has been submitted!') | ||
| // }) | ||
| // it('.click() - click on a DOM element', function () { | ||
| // // https://on.cypress.io/click | ||
| // cy.get('.action-btn').click() | ||
| // // You can click on 9 specific positions of an element: | ||
| // // ----------------------------------- | ||
| // // | topLeft top topRight | | ||
| // // | | | ||
| // // | | | ||
| // // | | | ||
| // // | left center right | | ||
| // // | | | ||
| // // | | | ||
| // // | | | ||
| // // | bottomLeft bottom bottomRight | | ||
| // // ----------------------------------- | ||
| // // clicking in the center of the element is the default | ||
| // cy.get('#action-canvas').click() | ||
| // cy.get('#action-canvas').click('topLeft') | ||
| // cy.get('#action-canvas').click('top') | ||
| // cy.get('#action-canvas').click('topRight') | ||
| // cy.get('#action-canvas').click('left') | ||
| // cy.get('#action-canvas').click('right') | ||
| // cy.get('#action-canvas').click('bottomLeft') | ||
| // cy.get('#action-canvas').click('bottom') | ||
| // cy.get('#action-canvas').click('bottomRight') | ||
| // // .click() accepts an x and y coordinate | ||
| // // that controls where the click occurs :) | ||
| // cy.get('#action-canvas') | ||
| // .click(80, 75) // click 80px on x coord and 75px on y coord | ||
| // .click(170, 75) | ||
| // .click(80, 165) | ||
| // .click(100, 185) | ||
| // .click(125, 190) | ||
| // .click(150, 185) | ||
| // .click(170, 165) | ||
| // // click multiple elements by passing multiple: true | ||
| // cy.get('.action-labels>.label').click({ multiple: true }) | ||
| // // Ignore error checking prior to clicking | ||
| // // like whether the element is visible, clickable or disabled | ||
| // // this button below is covered by another element. | ||
| // cy.get('.action-opacity>.btn').click({ force: true }) | ||
| // }) | ||
| // it('.dblclick() - double click on a DOM element', function () { | ||
| // // Our app has a listener on 'dblclick' event in our 'scripts.js' | ||
| // // that hides the div and shows an input on double click | ||
| // // https://on.cypress.io/dblclick | ||
| // cy.get('.action-div').dblclick().should('not.be.visible') | ||
| // cy.get('.action-input-hidden').should('be.visible') | ||
| // }) | ||
| // it('cy.check() - check a checkbox or radio element', function () { | ||
| // // By default, .check() will check all | ||
| // // matching checkbox or radio elements in succession, one after another | ||
| // // https://on.cypress.io/check | ||
| // cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') | ||
| // .check().should('be.checked') | ||
| // cy.get('.action-radios [type="radio"]').not('[disabled]') | ||
| // .check().should('be.checked') | ||
| // // .check() accepts a value argument | ||
| // // that checks only checkboxes or radios | ||
| // // with matching values | ||
| // cy.get('.action-radios [type="radio"]').check('radio1').should('be.checked') | ||
| // // .check() accepts an array of values | ||
| // // that checks only checkboxes or radios | ||
| // // with matching values | ||
| // cy.get('.action-multiple-checkboxes [type="checkbox"]') | ||
| // .check(['checkbox1', 'checkbox2']).should('be.checked') | ||
| // // Ignore error checking prior to checking | ||
| // // like whether the element is visible, clickable or disabled | ||
| // // this checkbox below is disabled. | ||
| // cy.get('.action-checkboxes [disabled]') | ||
| // .check({ force: true }).should('be.checked') | ||
| // cy.get('.action-radios [type="radio"]') | ||
| // .check('radio3', { force: true }).should('be.checked') | ||
| // }) | ||
| // it('.uncheck() - uncheck a checkbox element', function () { | ||
| // // By default, .uncheck() will uncheck all matching | ||
| // // checkbox elements in succession, one after another | ||
| // // https://on.cypress.io/uncheck | ||
| // cy.get('.action-check [type="checkbox"]') | ||
| // .not('[disabled]') | ||
| // .uncheck().should('not.be.checked') | ||
| // // .uncheck() accepts a value argument | ||
| // // that unchecks only checkboxes | ||
| // // with matching values | ||
| // cy.get('.action-check [type="checkbox"]') | ||
| // .check('checkbox1') | ||
| // .uncheck('checkbox1').should('not.be.checked') | ||
| // // .uncheck() accepts an array of values | ||
| // // that unchecks only checkboxes or radios | ||
| // // with matching values | ||
| // cy.get('.action-check [type="checkbox"]') | ||
| // .check(['checkbox1', 'checkbox3']) | ||
| // .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') | ||
| // // Ignore error checking prior to unchecking | ||
| // // like whether the element is visible, clickable or disabled | ||
| // // this checkbox below is disabled. | ||
| // cy.get('.action-check [disabled]') | ||
| // .uncheck({ force: true }).should('not.be.checked') | ||
| // }) | ||
| // it('.select() - select an option in a <select> element', function () { | ||
| // // https://on.cypress.io/select | ||
| // // Select option with matching text content | ||
| // cy.get('.action-select').select('apples') | ||
| // // Select option with matching value | ||
| // cy.get('.action-select').select('fr-bananas') | ||
| // // Select options with matching text content | ||
| // cy.get('.action-select-multiple') | ||
| // .select(['apples', 'oranges', 'bananas']) | ||
| // // Select options with matching values | ||
| // cy.get('.action-select-multiple') | ||
| // .select(['fr-apples', 'fr-oranges', 'fr-bananas']) | ||
| // }) | ||
| // it('.scrollIntoView() - scroll an element into view', function () { | ||
| // // https://on.cypress.io/scrollintoview | ||
| // // normally all of these buttons are hidden, because they're not within | ||
| // // the viewable area of their parent (we need to scroll to see them) | ||
| // cy.get('#scroll-horizontal button') | ||
| // .should('not.be.visible') | ||
| // // scroll the button into view, as if the user had scrolled | ||
| // cy.get('#scroll-horizontal button').scrollIntoView() | ||
| // .should('be.visible') | ||
| // cy.get('#scroll-vertical button') | ||
| // .should('not.be.visible') | ||
| // // Cypress handles the scroll direction needed | ||
| // cy.get('#scroll-vertical button').scrollIntoView() | ||
| // .should('be.visible') | ||
| // cy.get('#scroll-both button') | ||
| // .should('not.be.visible') | ||
| // // Cypress knows to scroll to the right and down | ||
| // cy.get('#scroll-both button').scrollIntoView() | ||
| // .should('be.visible') | ||
| // }) | ||
| // it('cy.scrollTo() - scroll the window or element to a position', function () { | ||
| // // https://on.cypress.io/scrollTo | ||
| // // You can scroll to 9 specific positions of an element: | ||
| // // ----------------------------------- | ||
| // // | topLeft top topRight | | ||
| // // | | | ||
| // // | | | ||
| // // | | | ||
| // // | left center right | | ||
| // // | | | ||
| // // | | | ||
| // // | | | ||
| // // | bottomLeft bottom bottomRight | | ||
| // // ----------------------------------- | ||
| // // if you chain .scrollTo() off of cy, we will | ||
| // // scroll the entire window | ||
| // cy.scrollTo('bottom') | ||
| // cy.get('#scrollable-horizontal').scrollTo('right') | ||
| // // or you can scroll to a specific coordinate: | ||
| // // (x axis, y axis) in pixels | ||
| // cy.get('#scrollable-vertical').scrollTo(250, 250) | ||
| // // or you can scroll to a specific percentage | ||
| // // of the (width, height) of the element | ||
| // cy.get('#scrollable-both').scrollTo('75%', '25%') | ||
| // // control the easing of the scroll (default is 'swing') | ||
| // cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) | ||
| // // control the duration of the scroll (in ms) | ||
| // cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) | ||
| // }) | ||
| // it('.trigger() - trigger an event on a DOM element', function () { | ||
| // // To interact with a range input (slider), we need to set its value and | ||
| // // then trigger the appropriate event to signal it has changed | ||
| // // Here, we invoke jQuery's val() method to set the value | ||
| // // and trigger the 'change' event | ||
| // // Note that some implementations may rely on the 'input' event, | ||
| // // which is fired as a user moves the slider, but is not supported | ||
| // // by some browsers | ||
| // // https://on.cypress.io/trigger | ||
| // cy.get('.trigger-input-range') | ||
| // .invoke('val', 25) | ||
| // .trigger('change') | ||
| // .get('input[type=range]').siblings('p') | ||
| // .should('have.text', '25') | ||
| // // See our example recipes for more examples of using trigger | ||
| // // https://on.cypress.io/examples | ||
| // }) | ||
| // }) | ||
| // context('Window', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/window') | ||
| // }) | ||
| // it('cy.window() - get the global window object', function () { | ||
| // // https://on.cypress.io/window | ||
| // cy.window().should('have.property', 'top') | ||
| // }) | ||
| // it('cy.document() - get the document object', function () { | ||
| // // https://on.cypress.io/document | ||
| // cy.document().should('have.property', 'charset').and('eq', 'UTF-8') | ||
| // }) | ||
| // it('cy.title() - get the title', function () { | ||
| // // https://on.cypress.io/title | ||
| // cy.title().should('include', 'Kitchen Sink') | ||
| // }) | ||
| // }) | ||
| // context('Viewport', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/viewport') | ||
| // }) | ||
| // it('cy.viewport() - set the viewport size and dimension', function () { | ||
| // cy.get('#navbar').should('be.visible') | ||
| // // https://on.cypress.io/viewport | ||
| // cy.viewport(320, 480) | ||
| // // the navbar should have collapse since our screen is smaller | ||
| // cy.get('#navbar').should('not.be.visible') | ||
| // cy.get('.navbar-toggle').should('be.visible').click() | ||
| // cy.get('.nav').find('a').should('be.visible') | ||
| // // lets see what our app looks like on a super large screen | ||
| // cy.viewport(2999, 2999) | ||
| // // cy.viewport() accepts a set of preset sizes | ||
| // // to easily set the screen to a device's width and height | ||
| // // We added a cy.wait() between each viewport change so you can see | ||
| // // the change otherwise it's a little too fast to see :) | ||
| // cy.viewport('macbook-15') | ||
| // cy.wait(200) | ||
| // cy.viewport('macbook-13') | ||
| // cy.wait(200) | ||
| // cy.viewport('macbook-11') | ||
| // cy.wait(200) | ||
| // cy.viewport('ipad-2') | ||
| // cy.wait(200) | ||
| // cy.viewport('ipad-mini') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-6+') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-6') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-5') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-4') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-3') | ||
| // cy.wait(200) | ||
| // // cy.viewport() accepts an orientation for all presets | ||
| // // the default orientation is 'portrait' | ||
| // cy.viewport('ipad-2', 'portrait') | ||
| // cy.wait(200) | ||
| // cy.viewport('iphone-4', 'landscape') | ||
| // cy.wait(200) | ||
| // // The viewport will be reset back to the default dimensions | ||
| // // in between tests (the default is set in cypress.json) | ||
| // }) | ||
| // }) | ||
| // context('Location', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/location') | ||
| // }) | ||
| // // We look at the url to make assertions | ||
| // // about the page's state | ||
| // it('cy.hash() - get the current URL hash', function () { | ||
| // // https://on.cypress.io/hash | ||
| // cy.hash().should('be.empty') | ||
| // }) | ||
| // it('cy.location() - get window.location', function () { | ||
| // // https://on.cypress.io/location | ||
| // cy.location().should(function (location) { | ||
| // expect(location.hash).to.be.empty | ||
| // expect(location.href).to.eq('https://example.cypress.io/commands/location') | ||
| // expect(location.host).to.eq('example.cypress.io') | ||
| // expect(location.hostname).to.eq('example.cypress.io') | ||
| // expect(location.origin).to.eq('https://example.cypress.io') | ||
| // expect(location.pathname).to.eq('/commands/location') | ||
| // expect(location.port).to.eq('') | ||
| // expect(location.protocol).to.eq('https:') | ||
| // expect(location.search).to.be.empty | ||
| // }) | ||
| // }) | ||
| // it('cy.url() - get the current URL', function () { | ||
| // // https://on.cypress.io/url | ||
| // cy.url().should('eq', 'https://example.cypress.io/commands/location') | ||
| // }) | ||
| // }) | ||
| // context('Navigation', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io') | ||
| // cy.get('.navbar-nav').contains('Commands').click() | ||
| // cy.get('.dropdown-menu').contains('Navigation').click() | ||
| // }) | ||
| // it('cy.go() - go back or forward in the browser\'s history', function () { | ||
| // cy.location('pathname').should('include', 'navigation') | ||
| // // https://on.cypress.io/go | ||
| // cy.go('back') | ||
| // cy.location('pathname').should('not.include', 'navigation') | ||
| // cy.go('forward') | ||
| // cy.location('pathname').should('include', 'navigation') | ||
| // // equivalent to clicking back | ||
| // cy.go(-1) | ||
| // cy.location('pathname').should('not.include', 'navigation') | ||
| // // equivalent to clicking forward | ||
| // cy.go(1) | ||
| // cy.location('pathname').should('include', 'navigation') | ||
| // }) | ||
| // it('cy.reload() - reload the page', function () { | ||
| // // https://on.cypress.io/reload | ||
| // cy.reload() | ||
| // // reload the page without using the cache | ||
| // cy.reload(true) | ||
| // }) | ||
| // it('cy.visit() - visit a remote url', function () { | ||
| // // Visit any sub-domain of your current domain | ||
| // // https://on.cypress.io/visit | ||
| // // Pass options to the visit | ||
| // cy.visit('https://example.cypress.io/commands/navigation', { | ||
| // timeout: 50000, // increase total time for the visit to resolve | ||
| // onBeforeLoad (contentWindow) { | ||
| // // contentWindow is the remote page's window object | ||
| // }, | ||
| // onLoad (contentWindow) { | ||
| // // contentWindow is the remote page's window object | ||
| // }, | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Assertions', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/assertions') | ||
| // }) | ||
| // describe('Implicit Assertions', function () { | ||
| // it('.should() - make an assertion about the current subject', function () { | ||
| // // https://on.cypress.io/should | ||
| // cy.get('.assertion-table') | ||
| // .find('tbody tr:last').should('have.class', 'success') | ||
| // }) | ||
| // it('.and() - chain multiple assertions together', function () { | ||
| // // https://on.cypress.io/and | ||
| // cy.get('.assertions-link') | ||
| // .should('have.class', 'active') | ||
| // .and('have.attr', 'href') | ||
| // .and('include', 'cypress.io') | ||
| // }) | ||
| // }) | ||
| // describe('Explicit Assertions', function () { | ||
| // // https://on.cypress.io/assertions | ||
| // it('expect - assert shape of an object', function () { | ||
| // const person = { | ||
| // name: 'Joe', | ||
| // age: 20, | ||
| // } | ||
| // expect(person).to.have.all.keys('name', 'age') | ||
| // }) | ||
| // it('expect - make an assertion about a specified subject', function () { | ||
| // // We can use Chai's BDD style assertions | ||
| // expect(true).to.be.true | ||
| // // Pass a function to should that can have any number | ||
| // // of explicit assertions within it. | ||
| // cy.get('.assertions-p').find('p') | ||
| // .should(function ($p) { | ||
| // // return an array of texts from all of the p's | ||
| // let texts = $p.map(function (i, el) { | ||
| // // https://on.cypress.io/$ | ||
| // return Cypress.$(el).text() | ||
| // }) | ||
| // // jquery map returns jquery object | ||
| // // and .get() convert this to simple array | ||
| // texts = texts.get() | ||
| // // array should have length of 3 | ||
| // expect(texts).to.have.length(3) | ||
| // // set this specific subject | ||
| // expect(texts).to.deep.eq([ | ||
| // 'Some text from first p', | ||
| // 'More text from second p', | ||
| // 'And even more text from third p', | ||
| // ]) | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Misc', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/misc') | ||
| // }) | ||
| // it('.end() - end the command chain', function () { | ||
| // // cy.end is useful when you want to end a chain of commands | ||
| // // and force Cypress to re-query from the root element | ||
| // // https://on.cypress.io/end | ||
| // cy.get('.misc-table').within(function () { | ||
| // // ends the current chain and yields null | ||
| // cy.contains('Cheryl').click().end() | ||
| // // queries the entire table again | ||
| // cy.contains('Charles').click() | ||
| // }) | ||
| // }) | ||
| // it('cy.exec() - execute a system command', function () { | ||
| // // cy.exec allows you to execute a system command. | ||
| // // so you can take actions necessary for your test, | ||
| // // but outside the scope of Cypress. | ||
| // // https://on.cypress.io/exec | ||
| // cy.exec('echo Jane Lane') | ||
| // .its('stdout').should('contain', 'Jane Lane') | ||
| // // we can use Cypress.platform string to | ||
| // // select appropriate command | ||
| // // https://on.cypress/io/platform | ||
| // cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) | ||
| // if (Cypress.platform === 'win32') { | ||
| // cy.exec('print cypress.json') | ||
| // .its('stderr').should('be.empty') | ||
| // } else { | ||
| // cy.exec('cat cypress.json') | ||
| // .its('stderr').should('be.empty') | ||
| // cy.exec('pwd') | ||
| // .its('code').should('eq', 0) | ||
| // } | ||
| // }) | ||
| // it('cy.focused() - get the DOM element that has focus', function () { | ||
| // // https://on.cypress.io/focused | ||
| // cy.get('.misc-form').find('#name').click() | ||
| // cy.focused().should('have.id', 'name') | ||
| // cy.get('.misc-form').find('#description').click() | ||
| // cy.focused().should('have.id', 'description') | ||
| // }) | ||
| // it('cy.screenshot() - take a screenshot', function () { | ||
| // // https://on.cypress.io/screenshot | ||
| // cy.screenshot('my-image') | ||
| // }) | ||
| // it('cy.wrap() - wrap an object', function () { | ||
| // // https://on.cypress.io/wrap | ||
| // cy.wrap({ foo: 'bar' }) | ||
| // .should('have.property', 'foo') | ||
| // .and('include', 'bar') | ||
| // }) | ||
| // }) | ||
| // context('Connectors', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/connectors') | ||
| // }) | ||
| // it('.each() - iterate over an array of elements', function () { | ||
| // // https://on.cypress.io/each | ||
| // cy.get('.connectors-each-ul>li') | ||
| // .each(function ($el, index, $list) { | ||
| // console.log($el, index, $list) | ||
| // }) | ||
| // }) | ||
| // it('.its() - get properties on the current subject', function () { | ||
| // // https://on.cypress.io/its | ||
| // cy.get('.connectors-its-ul>li') | ||
| // // calls the 'length' property yielding that value | ||
| // .its('length') | ||
| // .should('be.gt', 2) | ||
| // }) | ||
| // it('.invoke() - invoke a function on the current subject', function () { | ||
| // // our div is hidden in our script.js | ||
| // // $('.connectors-div').hide() | ||
| // // https://on.cypress.io/invoke | ||
| // cy.get('.connectors-div').should('be.hidden') | ||
| // // call the jquery method 'show' on the 'div.container' | ||
| // .invoke('show') | ||
| // .should('be.visible') | ||
| // }) | ||
| // it('.spread() - spread an array as individual args to callback function', function () { | ||
| // // https://on.cypress.io/spread | ||
| // let arr = ['foo', 'bar', 'baz'] | ||
| // cy.wrap(arr).spread(function (foo, bar, baz) { | ||
| // expect(foo).to.eq('foo') | ||
| // expect(bar).to.eq('bar') | ||
| // expect(baz).to.eq('baz') | ||
| // }) | ||
| // }) | ||
| // it('.then() - invoke a callback function with the current subject', function () { | ||
| // // https://on.cypress.io/then | ||
| // cy.get('.connectors-list>li').then(function ($lis) { | ||
| // expect($lis).to.have.length(3) | ||
| // expect($lis.eq(0)).to.contain('Walk the dog') | ||
| // expect($lis.eq(1)).to.contain('Feed the cat') | ||
| // expect($lis.eq(2)).to.contain('Write JavaScript') | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Aliasing', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/aliasing') | ||
| // }) | ||
| // // We alias a DOM element for use later | ||
| // // We don't have to traverse to the element | ||
| // // later in our code, we just reference it with @ | ||
| // it('.as() - alias a route or DOM element for later use', function () { | ||
| // // this is a good use case for an alias, | ||
| // // we don't want to write this long traversal again | ||
| // // https://on.cypress.io/as | ||
| // cy.get('.as-table').find('tbody>tr') | ||
| // .first().find('td').first().find('button').as('firstBtn') | ||
| // // maybe do some more testing here... | ||
| // // when we reference the alias, we place an | ||
| // // @ in front of it's name | ||
| // cy.get('@firstBtn').click() | ||
| // cy.get('@firstBtn') | ||
| // .should('have.class', 'btn-success') | ||
| // .and('contain', 'Changed') | ||
| // }) | ||
| // }) | ||
| // context('Waiting', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/waiting') | ||
| // }) | ||
| // // BE CAREFUL of adding unnecessary wait times. | ||
| // // https://on.cypress.io/wait | ||
| // it('cy.wait() - wait for a specific amount of time', function () { | ||
| // cy.get('.wait-input1').type('Wait 1000ms after typing') | ||
| // cy.wait(1000) | ||
| // cy.get('.wait-input2').type('Wait 1000ms after typing') | ||
| // cy.wait(1000) | ||
| // cy.get('.wait-input3').type('Wait 1000ms after typing') | ||
| // cy.wait(1000) | ||
| // }) | ||
| // // Waiting for a specific resource to resolve | ||
| // // is covered within the cy.route() test below | ||
| // }) | ||
| // context('Network Requests', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/network-requests') | ||
| // }) | ||
| // // Manage AJAX / XHR requests in your app | ||
| // it('cy.server() - control behavior of network requests and responses', function () { | ||
| // // https://on.cypress.io/server | ||
| // cy.server().should(function (server) { | ||
| // // the default options on server | ||
| // // you can override any of these options | ||
| // expect(server.delay).to.eq(0) | ||
| // expect(server.method).to.eq('GET') | ||
| // expect(server.status).to.eq(200) | ||
| // expect(server.headers).to.be.null | ||
| // expect(server.response).to.be.null | ||
| // expect(server.onRequest).to.be.undefined | ||
| // expect(server.onResponse).to.be.undefined | ||
| // expect(server.onAbort).to.be.undefined | ||
| // // These options control the server behavior | ||
| // // affecting all requests | ||
| // // pass false to disable existing route stubs | ||
| // expect(server.enable).to.be.true | ||
| // // forces requests that don't match your routes to 404 | ||
| // expect(server.force404).to.be.false | ||
| // // whitelists requests from ever being logged or stubbed | ||
| // expect(server.whitelist).to.be.a('function') | ||
| // }) | ||
| // cy.server({ | ||
| // method: 'POST', | ||
| // delay: 1000, | ||
| // status: 422, | ||
| // response: {}, | ||
| // }) | ||
| // // any route commands will now inherit the above options | ||
| // // from the server. anything we pass specifically | ||
| // // to route will override the defaults though. | ||
| // }) | ||
| // it('cy.request() - make an XHR request', function () { | ||
| // // https://on.cypress.io/request | ||
| // cy.request('https://jsonplaceholder.typicode.com/comments') | ||
| // .should(function (response) { | ||
| // expect(response.status).to.eq(200) | ||
| // expect(response.body).to.have.length(500) | ||
| // expect(response).to.have.property('headers') | ||
| // expect(response).to.have.property('duration') | ||
| // }) | ||
| // }) | ||
| // it('cy.route() - route responses to matching requests', function () { | ||
| // let message = 'whoa, this comment doesn\'t exist' | ||
| // cy.server() | ||
| // // **** GET comments route **** | ||
| // // https://on.cypress.io/route | ||
| // cy.route(/comments\/1/).as('getComment') | ||
| // // we have code that fetches a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.network-btn').click() | ||
| // // **** Wait **** | ||
| // // Wait for a specific resource to resolve | ||
| // // continuing to the next command | ||
| // // https://on.cypress.io/wait | ||
| // cy.wait('@getComment').its('status').should('eq', 200) | ||
| // // **** POST comment route **** | ||
| // // Specify the route to listen to method 'POST' | ||
| // cy.route('POST', '/comments').as('postComment') | ||
| // // we have code that posts a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.network-post').click() | ||
| // cy.wait('@postComment') | ||
| // // get the route | ||
| // cy.get('@postComment').then(function (xhr) { | ||
| // expect(xhr.requestBody).to.include('email') | ||
| // expect(xhr.requestHeaders).to.have.property('Content-Type') | ||
| // expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()') | ||
| // }) | ||
| // // **** Stubbed PUT comment route **** | ||
| // cy.route({ | ||
| // method: 'PUT', | ||
| // url: /comments\/\d+/, | ||
| // status: 404, | ||
| // response: { error: message }, | ||
| // delay: 500, | ||
| // }).as('putComment') | ||
| // // we have code that puts a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.network-put').click() | ||
| // cy.wait('@putComment') | ||
| // // our 404 statusCode logic in scripts.js executed | ||
| // cy.get('.network-put-comment').should('contain', message) | ||
| // }) | ||
| // }) | ||
| // context('Files', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/files') | ||
| // }) | ||
| // it('cy.fixture() - load a fixture', function () { | ||
| // // Instead of writing a response inline you can | ||
| // // connect a response with a fixture file | ||
| // // located in fixtures folder. | ||
| // cy.server() | ||
| // // https://on.cypress.io/fixture | ||
| // cy.fixture('example.json').as('comment') | ||
| // cy.route(/comments/, '@comment').as('getComment') | ||
| // // we have code that gets a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.fixture-btn').click() | ||
| // cy.wait('@getComment').its('responseBody') | ||
| // .should('have.property', 'name') | ||
| // .and('include', 'Using fixtures to represent data') | ||
| // // you can also just write the fixture in the route | ||
| // cy.route(/comments/, 'fixture:example.json').as('getComment') | ||
| // // we have code that gets a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.fixture-btn').click() | ||
| // cy.wait('@getComment').its('responseBody') | ||
| // .should('have.property', 'name') | ||
| // .and('include', 'Using fixtures to represent data') | ||
| // // or write fx to represent fixture | ||
| // // by default it assumes it's .json | ||
| // cy.route(/comments/, 'fx:example').as('getComment') | ||
| // // we have code that gets a comment when | ||
| // // the button is clicked in scripts.js | ||
| // cy.get('.fixture-btn').click() | ||
| // cy.wait('@getComment').its('responseBody') | ||
| // .should('have.property', 'name') | ||
| // .and('include', 'Using fixtures to represent data') | ||
| // }) | ||
| // it('cy.readFile() - read a files contents', function () { | ||
| // // You can read a file and yield its contents | ||
| // // The filePath is relative to your project's root. | ||
| // // https://on.cypress.io/readfile | ||
| // cy.readFile('cypress.json').then(function (json) { | ||
| // expect(json).to.be.an('object') | ||
| // }) | ||
| // }) | ||
| // it('cy.writeFile() - write to a file', function () { | ||
| // // You can write to a file with the specified contents | ||
| // // Use a response from a request to automatically | ||
| // // generate a fixture file for use later | ||
| // cy.request('https://jsonplaceholder.typicode.com/users') | ||
| // .then(function (response) { | ||
| // // https://on.cypress.io/writefile | ||
| // cy.writeFile('cypress/fixtures/users.json', response.body) | ||
| // }) | ||
| // cy.fixture('users').should(function (users) { | ||
| // expect(users[0].name).to.exist | ||
| // }) | ||
| // // JavaScript arrays and objects are stringified and formatted into text. | ||
| // cy.writeFile('cypress/fixtures/profile.json', { | ||
| // id: 8739, | ||
| // name: 'Jane', | ||
| // email: 'jane@example.com', | ||
| // }) | ||
| // cy.fixture('profile').should(function (profile) { | ||
| // expect(profile.name).to.eq('Jane') | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Local Storage', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/commands/local-storage') | ||
| // }) | ||
| // // Although local storage is automatically cleared | ||
| // // to maintain a clean state in between tests | ||
| // // sometimes we need to clear the local storage manually | ||
| // it('cy.clearLocalStorage() - clear all data in local storage', function () { | ||
| // // https://on.cypress.io/clearlocalstorage | ||
| // cy.get('.ls-btn').click().should(function () { | ||
| // expect(localStorage.getItem('prop1')).to.eq('red') | ||
| // expect(localStorage.getItem('prop2')).to.eq('blue') | ||
| // expect(localStorage.getItem('prop3')).to.eq('magenta') | ||
| // }) | ||
| // // clearLocalStorage() yields the localStorage object | ||
| // cy.clearLocalStorage().should(function (ls) { | ||
| // expect(ls.getItem('prop1')).to.be.null | ||
| // expect(ls.getItem('prop2')).to.be.null | ||
| // expect(ls.getItem('prop3')).to.be.null | ||
| // }) | ||
| // // **** Clear key matching string in Local Storage **** | ||
| // cy.get('.ls-btn').click().should(function () { | ||
| // expect(localStorage.getItem('prop1')).to.eq('red') | ||
| // expect(localStorage.getItem('prop2')).to.eq('blue') | ||
| // expect(localStorage.getItem('prop3')).to.eq('magenta') | ||
| // }) | ||
| // cy.clearLocalStorage('prop1').should(function (ls) { | ||
| // expect(ls.getItem('prop1')).to.be.null | ||
| // expect(ls.getItem('prop2')).to.eq('blue') | ||
| // expect(ls.getItem('prop3')).to.eq('magenta') | ||
| // }) | ||
| // // **** Clear key's matching regex in Local Storage **** | ||
| // cy.get('.ls-btn').click().should(function () { | ||
| // expect(localStorage.getItem('prop1')).to.eq('red') | ||
| // expect(localStorage.getItem('prop2')).to.eq('blue') | ||
| // expect(localStorage.getItem('prop3')).to.eq('magenta') | ||
| // }) | ||
| // cy.clearLocalStorage(/prop1|2/).should(function (ls) { | ||
| // expect(ls.getItem('prop1')).to.be.null | ||
| // expect(ls.getItem('prop2')).to.be.null | ||
| // expect(ls.getItem('prop3')).to.eq('magenta') | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Cookies', function () { | ||
| // beforeEach(function () { | ||
| // Cypress.Cookies.debug(true) | ||
| // cy.visit('https://example.cypress.io/commands/cookies') | ||
| // // clear cookies again after visiting to remove | ||
| // // any 3rd party cookies picked up such as cloudflare | ||
| // cy.clearCookies() | ||
| // }) | ||
| // it('cy.getCookie() - get a browser cookie', function () { | ||
| // // https://on.cypress.io/getcookie | ||
| // cy.get('#getCookie .set-a-cookie').click() | ||
| // // cy.getCookie() yields a cookie object | ||
| // cy.getCookie('token').should('have.property', 'value', '123ABC') | ||
| // }) | ||
| // it('cy.getCookies() - get browser cookies', function () { | ||
| // // https://on.cypress.io/getcookies | ||
| // cy.getCookies().should('be.empty') | ||
| // cy.get('#getCookies .set-a-cookie').click() | ||
| // // cy.getCookies() yields an array of cookies | ||
| // cy.getCookies().should('have.length', 1).should(function (cookies) { | ||
| // // each cookie has these properties | ||
| // expect(cookies[0]).to.have.property('name', 'token') | ||
| // expect(cookies[0]).to.have.property('value', '123ABC') | ||
| // expect(cookies[0]).to.have.property('httpOnly', false) | ||
| // expect(cookies[0]).to.have.property('secure', false) | ||
| // expect(cookies[0]).to.have.property('domain') | ||
| // expect(cookies[0]).to.have.property('path') | ||
| // }) | ||
| // }) | ||
| // it('cy.setCookie() - set a browser cookie', function () { | ||
| // // https://on.cypress.io/setcookie | ||
| // cy.getCookies().should('be.empty') | ||
| // cy.setCookie('foo', 'bar') | ||
| // // cy.getCookie() yields a cookie object | ||
| // cy.getCookie('foo').should('have.property', 'value', 'bar') | ||
| // }) | ||
| // it('cy.clearCookie() - clear a browser cookie', function () { | ||
| // // https://on.cypress.io/clearcookie | ||
| // cy.getCookie('token').should('be.null') | ||
| // cy.get('#clearCookie .set-a-cookie').click() | ||
| // cy.getCookie('token').should('have.property', 'value', '123ABC') | ||
| // // cy.clearCookies() yields null | ||
| // cy.clearCookie('token').should('be.null') | ||
| // cy.getCookie('token').should('be.null') | ||
| // }) | ||
| // it('cy.clearCookies() - clear browser cookies', function () { | ||
| // // https://on.cypress.io/clearcookies | ||
| // cy.getCookies().should('be.empty') | ||
| // cy.get('#clearCookies .set-a-cookie').click() | ||
| // cy.getCookies().should('have.length', 1) | ||
| // // cy.clearCookies() yields null | ||
| // cy.clearCookies() | ||
| // cy.getCookies().should('be.empty') | ||
| // }) | ||
| // }) | ||
| // context('Spies, Stubs, and Clock', function () { | ||
| // it('cy.spy() - wrap a method in a spy', function () { | ||
| // // https://on.cypress.io/spy | ||
| // cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') | ||
| // let obj = { | ||
| // foo () {}, | ||
| // } | ||
| // let spy = cy.spy(obj, 'foo').as('anyArgs') | ||
| // obj.foo() | ||
| // expect(spy).to.be.called | ||
| // }) | ||
| // it('cy.stub() - create a stub and/or replace a function with a stub', function () { | ||
| // // https://on.cypress.io/stub | ||
| // cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') | ||
| // let obj = { | ||
| // foo () {}, | ||
| // } | ||
| // let stub = cy.stub(obj, 'foo').as('foo') | ||
| // obj.foo('foo', 'bar') | ||
| // expect(stub).to.be.called | ||
| // }) | ||
| // it('cy.clock() - control time in the browser', function () { | ||
| // // create the date in UTC so its always the same | ||
| // // no matter what local timezone the browser is running in | ||
| // let now = new Date(Date.UTC(2017, 2, 14)).getTime() | ||
| // // https://on.cypress.io/clock | ||
| // cy.clock(now) | ||
| // cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') | ||
| // cy.get('#clock-div').click() | ||
| // .should('have.text', '1489449600') | ||
| // }) | ||
| // it('cy.tick() - move time in the browser', function () { | ||
| // // create the date in UTC so its always the same | ||
| // // no matter what local timezone the browser is running in | ||
| // let now = new Date(Date.UTC(2017, 2, 14)).getTime() | ||
| // // https://on.cypress.io/tick | ||
| // cy.clock(now) | ||
| // cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') | ||
| // cy.get('#tick-div').click() | ||
| // .should('have.text', '1489449600') | ||
| // cy.tick(10000) // 10 seconds passed | ||
| // cy.get('#tick-div').click() | ||
| // .should('have.text', '1489449610') | ||
| // }) | ||
| // }) | ||
| // context('Utilities', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/utilities') | ||
| // }) | ||
| // it('Cypress._.method() - call a lodash method', function () { | ||
| // // use the _.chain, _.map, _.take, and _.value functions | ||
| // // https://on.cypress.io/_ | ||
| // cy.request('https://jsonplaceholder.typicode.com/users') | ||
| // .then(function (response) { | ||
| // let ids = Cypress._.chain(response.body).map('id').take(3).value() | ||
| // expect(ids).to.deep.eq([1, 2, 3]) | ||
| // }) | ||
| // }) | ||
| // it('Cypress.$(selector) - call a jQuery method', function () { | ||
| // // https://on.cypress.io/$ | ||
| // let $li = Cypress.$('.utility-jquery li:first') | ||
| // cy.wrap($li) | ||
| // .should('not.have.class', 'active') | ||
| // .click() | ||
| // .should('have.class', 'active') | ||
| // }) | ||
| // it('Cypress.moment() - format or parse dates using a moment method', function () { | ||
| // // use moment's format function | ||
| // // https://on.cypress.io/cypress-moment | ||
| // let time = Cypress.moment().utc('2014-04-25T19:38:53.196Z').format('h:mm A') | ||
| // cy.get('.utility-moment').contains('3:38 PM') | ||
| // .should('have.class', 'badge') | ||
| // }) | ||
| // it('Cypress.Blob.method() - blob utilities and base64 string conversion', function () { | ||
| // cy.get('.utility-blob').then(function ($div) { | ||
| // // https://on.cypress.io/blob | ||
| // // https://github.com/nolanlawson/blob-util#imgSrcToDataURL | ||
| // // get the dataUrl string for the javascript-logo | ||
| // return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous') | ||
| // .then(function (dataUrl) { | ||
| // // create an <img> element and set its src to the dataUrl | ||
| // let img = Cypress.$('<img />', { src: dataUrl }) | ||
| // // need to explicitly return cy here since we are initially returning | ||
| // // the Cypress.Blob.imgSrcToDataURL promise to our test | ||
| // // append the image | ||
| // $div.append(img) | ||
| // cy.get('.utility-blob img').click() | ||
| // .should('have.attr', 'src', dataUrl) | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // it('new Cypress.Promise(function) - instantiate a bluebird promise', function () { | ||
| // // https://on.cypress.io/promise | ||
| // let waited = false | ||
| // function waitOneSecond () { | ||
| // // return a promise that resolves after 1 second | ||
| // return new Cypress.Promise(function (resolve, reject) { | ||
| // setTimeout(function () { | ||
| // // set waited to true | ||
| // waited = true | ||
| // // resolve with 'foo' string | ||
| // resolve('foo') | ||
| // }, 1000) | ||
| // }) | ||
| // } | ||
| // cy.then(function () { | ||
| // // return a promise to cy.then() that | ||
| // // is awaited until it resolves | ||
| // return waitOneSecond().then(function (str) { | ||
| // expect(str).to.eq('foo') | ||
| // expect(waited).to.be.true | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Cypress.config()', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/cypress-api/config') | ||
| // }) | ||
| // it('Cypress.config() - get and set configuration options', function () { | ||
| // // https://on.cypress.io/config | ||
| // let myConfig = Cypress.config() | ||
| // expect(myConfig).to.have.property('animationDistanceThreshold', 5) | ||
| // expect(myConfig).to.have.property('baseUrl', null) | ||
| // expect(myConfig).to.have.property('defaultCommandTimeout', 4000) | ||
| // expect(myConfig).to.have.property('requestTimeout', 5000) | ||
| // expect(myConfig).to.have.property('responseTimeout', 30000) | ||
| // expect(myConfig).to.have.property('viewportHeight', 660) | ||
| // expect(myConfig).to.have.property('viewportWidth', 1000) | ||
| // expect(myConfig).to.have.property('pageLoadTimeout', 60000) | ||
| // expect(myConfig).to.have.property('waitForAnimations', true) | ||
| // expect(Cypress.config('pageLoadTimeout')).to.eq(60000) | ||
| // // this will change the config for the rest of your tests! | ||
| // Cypress.config('pageLoadTimeout', 20000) | ||
| // expect(Cypress.config('pageLoadTimeout')).to.eq(20000) | ||
| // Cypress.config('pageLoadTimeout', 60000) | ||
| // }) | ||
| // }) | ||
| // context('Cypress.env()', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/cypress-api/env') | ||
| // }) | ||
| // // We can set environment variables for highly dynamic values | ||
| // // https://on.cypress.io/environment-variables | ||
| // it('Cypress.env() - get environment variables', function () { | ||
| // // https://on.cypress.io/env | ||
| // // set multiple environment variables | ||
| // Cypress.env({ | ||
| // host: 'veronica.dev.local', | ||
| // api_server: 'http://localhost:8888/v1/', | ||
| // }) | ||
| // // get environment variable | ||
| // expect(Cypress.env('host')).to.eq('veronica.dev.local') | ||
| // // set environment variable | ||
| // Cypress.env('api_server', 'http://localhost:8888/v2/') | ||
| // expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/') | ||
| // // get all environment variable | ||
| // expect(Cypress.env()).to.have.property('host', 'veronica.dev.local') | ||
| // expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/') | ||
| // }) | ||
| // }) | ||
| // context('Cypress.Cookies', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/cypress-api/cookies') | ||
| // }) | ||
| // // https://on.cypress.io/cookies | ||
| // it('Cypress.Cookies.debug() - enable or disable debugging', function () { | ||
| // Cypress.Cookies.debug(true) | ||
| // // Cypress will now log in the console when | ||
| // // cookies are set or cleared | ||
| // cy.setCookie('fakeCookie', '123ABC') | ||
| // cy.clearCookie('fakeCookie') | ||
| // cy.setCookie('fakeCookie', '123ABC') | ||
| // cy.clearCookie('fakeCookie') | ||
| // cy.setCookie('fakeCookie', '123ABC') | ||
| // }) | ||
| // it('Cypress.Cookies.preserveOnce() - preserve cookies by key', function () { | ||
| // // normally cookies are reset after each test | ||
| // cy.getCookie('fakeCookie').should('not.be.ok') | ||
| // // preserving a cookie will not clear it when | ||
| // // the next test starts | ||
| // cy.setCookie('lastCookie', '789XYZ') | ||
| // Cypress.Cookies.preserveOnce('lastCookie') | ||
| // }) | ||
| // it('Cypress.Cookies.defaults() - set defaults for all cookies', function () { | ||
| // // now any cookie with the name 'session_id' will | ||
| // // not be cleared before each new test runs | ||
| // Cypress.Cookies.defaults({ | ||
| // whitelist: 'session_id', | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // context('Cypress.dom', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/cypress-api/dom') | ||
| // }) | ||
| // // https://on.cypress.io/dom | ||
| // it('Cypress.dom.isHidden() - determine if a DOM element is hidden', function () { | ||
| // let hiddenP = Cypress.$('.dom-p p.hidden').get(0) | ||
| // let visibleP = Cypress.$('.dom-p p.visible').get(0) | ||
| // // our first paragraph has css class 'hidden' | ||
| // expect(Cypress.dom.isHidden(hiddenP)).to.be.true | ||
| // expect(Cypress.dom.isHidden(visibleP)).to.be.false | ||
| // }) | ||
| // }) | ||
| // context('Cypress.Server', function () { | ||
| // beforeEach(function () { | ||
| // cy.visit('https://example.cypress.io/cypress-api/server') | ||
| // }) | ||
| // // Permanently override server options for | ||
| // // all instances of cy.server() | ||
| // // https://on.cypress.io/cypress-server | ||
| // it('Cypress.Server.defaults() - change default config of server', function () { | ||
| // Cypress.Server.defaults({ | ||
| // delay: 0, | ||
| // force404: false, | ||
| // whitelist (xhr) { | ||
| // // handle custom logic for whitelisting | ||
| // }, | ||
| // }) | ||
| // }) | ||
| // }) | ||
| // }) |
| // *********************************************************** | ||
| // This example plugins/index.js can be used to load plugins | ||
| // | ||
| // You can change the location of this file or turn off loading | ||
| // the plugins file with the 'pluginsFile' configuration option. | ||
| // | ||
| // You can read more here: | ||
| // https://on.cypress.io/plugins-guide | ||
| // *********************************************************** | ||
| // This function is called when a project is opened or re-opened (e.g. due to | ||
| // the project's config changing) | ||
| module.exports = (on, config) => { | ||
| // `on` is used to hook into various events Cypress emits | ||
| // `config` is the resolved Cypress config | ||
| } |
| // *********************************************** | ||
| // This example commands.js shows you how to | ||
| // create various custom commands and overwrite | ||
| // existing commands. | ||
| // | ||
| // For more comprehensive examples of custom | ||
| // commands please read more here: | ||
| // https://on.cypress.io/custom-commands | ||
| // *********************************************** | ||
| // | ||
| // | ||
| // -- This is a parent command -- | ||
| // Cypress.Commands.add("login", (email, password) => { ... }) | ||
| // | ||
| // | ||
| // -- This is a child command -- | ||
| // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) | ||
| // | ||
| // | ||
| // -- This is a dual command -- | ||
| // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) | ||
| // | ||
| // | ||
| // -- This is will overwrite an existing command -- | ||
| // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) |
| // *********************************************************** | ||
| // This example support/index.js is processed and | ||
| // loaded automatically before your test files. | ||
| // | ||
| // This is a great place to put global configuration and | ||
| // behavior that modifies Cypress. | ||
| // | ||
| // You can change the location of this file or turn off | ||
| // automatically serving support files with the | ||
| // 'supportFile' configuration option. | ||
| // | ||
| // You can read more here: | ||
| // https://on.cypress.io/configuration | ||
| // *********************************************************** | ||
| // Import commands.js using ES2015 syntax: | ||
| import './commands' | ||
| // Alternatively you can use CommonJS syntax: | ||
| // require('./commands') |
Sorry, the diff of this file is not supported yet
| (function webpackUniversalModuleDefinition(root, factory) { | ||
| if(typeof exports === 'object' && typeof module === 'object') | ||
| module.exports = factory(); | ||
| else if(typeof define === 'function' && define.amd) | ||
| define([], factory); | ||
| else if(typeof exports === 'object') | ||
| exports["MachinateInspector"] = factory(); | ||
| else | ||
| root["MachinateInspector"] = factory(); | ||
| })(typeof self !== 'undefined' ? self : this, function() { | ||
| return /******/ (function(modules) { // webpackBootstrap | ||
| /******/ // The module cache | ||
| /******/ var installedModules = {}; | ||
| /******/ | ||
| /******/ // The require function | ||
| /******/ function __webpack_require__(moduleId) { | ||
| /******/ | ||
| /******/ // Check if module is in cache | ||
| /******/ if(installedModules[moduleId]) { | ||
| /******/ return installedModules[moduleId].exports; | ||
| /******/ } | ||
| /******/ // Create a new module (and put it into the cache) | ||
| /******/ var module = installedModules[moduleId] = { | ||
| /******/ i: moduleId, | ||
| /******/ l: false, | ||
| /******/ exports: {} | ||
| /******/ }; | ||
| /******/ | ||
| /******/ // Execute the module function | ||
| /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | ||
| /******/ | ||
| /******/ // Flag the module as loaded | ||
| /******/ module.l = true; | ||
| /******/ | ||
| /******/ // Return the exports of the module | ||
| /******/ return module.exports; | ||
| /******/ } | ||
| /******/ | ||
| /******/ | ||
| /******/ // expose the modules object (__webpack_modules__) | ||
| /******/ __webpack_require__.m = modules; | ||
| /******/ | ||
| /******/ // expose the module cache | ||
| /******/ __webpack_require__.c = installedModules; | ||
| /******/ | ||
| /******/ // define getter function for harmony exports | ||
| /******/ __webpack_require__.d = function(exports, name, getter) { | ||
| /******/ if(!__webpack_require__.o(exports, name)) { | ||
| /******/ Object.defineProperty(exports, name, { | ||
| /******/ configurable: false, | ||
| /******/ enumerable: true, | ||
| /******/ get: getter | ||
| /******/ }); | ||
| /******/ } | ||
| /******/ }; | ||
| /******/ | ||
| /******/ // getDefaultExport function for compatibility with non-harmony modules | ||
| /******/ __webpack_require__.n = function(module) { | ||
| /******/ var getter = module && module.__esModule ? | ||
| /******/ function getDefault() { return module['default']; } : | ||
| /******/ function getModuleExports() { return module; }; | ||
| /******/ __webpack_require__.d(getter, 'a', getter); | ||
| /******/ return getter; | ||
| /******/ }; | ||
| /******/ | ||
| /******/ // Object.prototype.hasOwnProperty.call | ||
| /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; | ||
| /******/ | ||
| /******/ // __webpack_public_path__ | ||
| /******/ __webpack_require__.p = ""; | ||
| /******/ | ||
| /******/ // Load entry module and return exports | ||
| /******/ return __webpack_require__(__webpack_require__.s = 17); | ||
| /******/ }) | ||
| /************************************************************************/ | ||
| /******/ ({ | ||
| /***/ 17: | ||
| /***/ (function(module, exports) { | ||
| throw new Error("Module build failed: Error: ENOENT: no such file or directory, open '/Users/pratik/Projects/sandbox/machinate/plugins/inspector/src/index.js'"); | ||
| /***/ }) | ||
| /******/ }); | ||
| }); | ||
| //# sourceMappingURL=lib.js.map |
| {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 27954c96d95acc002f70"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA","file":"lib.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"MachinateInspector\"] = factory();\n\telse\n\t\troot[\"MachinateInspector\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 17);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 27954c96d95acc002f70"],"sourceRoot":""} |
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| <title>Demo</title> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| <script src="./build/lib.js"></script> | ||
| <script src="./../../build/lib.js"></script> | ||
| <!-- <script src="https://unpkg.com/machinate"></script> --> | ||
| <script src="https://unpkg.com/react@16/umd/react.development.js"></script> | ||
| <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> | ||
| <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="text/babel"> | ||
| const { Inspector } = window.MachinateInspector; | ||
| const { Machinate, States, Submachine } = window.Machinate; | ||
| // const scheme = { Auth: ["LoggedOut", "LoggedIn"] }; | ||
| // const initial = { Auth: "LoggedOut" }; | ||
| // const App = () => <Machinate scheme={scheme} initial={initial}> | ||
| // <Inspector /> | ||
| // <States for="Auth" | ||
| // LoggedOut={() => <h1>Logged out.</h1>} | ||
| // LoggedIn={(data) => <h1>Logged in - {data}.</h1>} | ||
| // /> | ||
| // </Machinate> | ||
| class Demo extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
| this.scheme = { | ||
| Items: { states: ["List"] }, | ||
| Visibility: { states: ["Show", "Hide"], deps: { "Items.List": "Show" } }, | ||
| Block: { states: ["Element"] }, | ||
| BlockVisibility: { | ||
| states: ["Show", "Hide"], | ||
| deps: { "Block.Element": "Show" } | ||
| }, | ||
| Mode: { | ||
| states: ["View", "Edit"], | ||
| deps: { "BlockVisibility.Show": "View" } | ||
| } | ||
| }; | ||
| this.initialState = { | ||
| Items: { state: "List", data: ["Block"] }, | ||
| Visibility: "Show" | ||
| }; | ||
| this.initialBlockState = data => { | ||
| console.log("generating initial submachine state", {data}); | ||
| return { | ||
| Block: { | ||
| state: "Element", | ||
| data | ||
| }, | ||
| BlockVisibility: { | ||
| state: "Show" | ||
| }, | ||
| Mode: { | ||
| state: "View" | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| render() { | ||
| return ( | ||
| <Machinate | ||
| scheme={this.scheme} | ||
| initial={this.initialState} | ||
| ref={ref => (window.machine = ref.state.machine)} | ||
| key="main" | ||
| > | ||
| <Inspector /> | ||
| <h2 data-test="list-header">My List</h2> | ||
| <States | ||
| for="Items" | ||
| List={(data, { go }) => ( | ||
| <States | ||
| for="Visibility" | ||
| Show={() => { | ||
| return ( | ||
| <React.Fragment> | ||
| <button | ||
| data-test="toggle-visibility" | ||
| onClick={go("Visibility.Hide")} | ||
| > | ||
| Toggle Show/Hide | ||
| </button> | ||
| <button | ||
| data-test="add-block" | ||
| onClick={go("Items.List", [...data, data.length + 1])} | ||
| > | ||
| Add block | ||
| </button> | ||
| <div className="list-container"> | ||
| {data.map((num, idx) => ( | ||
| <Submachine | ||
| key={"item-" + idx} | ||
| id={"item-" + idx} | ||
| initial={this.initialBlockState(num)} | ||
| > | ||
| <States | ||
| for="Block" | ||
| Element={(num, { transition }) => { | ||
| return <States | ||
| for="Mode" | ||
| View={() => ( | ||
| <div className="block"> | ||
| <div data-test={"text-" + idx}>{num}</div> | ||
| <button | ||
| data-test={"change-mode-" + idx} | ||
| onClick={() => | ||
| transition("Mode", "Edit", num) | ||
| } | ||
| > | ||
| Edit | ||
| </button> | ||
| </div> | ||
| )} | ||
| Edit={(editedNum, { go }) => { | ||
| return ( | ||
| <div className="block edit"> | ||
| <div> | ||
| <input | ||
| data-test={"input-" + idx} | ||
| key={idx} | ||
| value={editedNum} | ||
| onChange={e => | ||
| go("Mode.Edit", e.target.value)() | ||
| } | ||
| /> | ||
| </div> | ||
| <button | ||
| data-test={"save-" + idx} | ||
| onClick={e => { | ||
| const newData = [...data]; | ||
| newData[idx] = editedNum; | ||
| go("Items.List", newData)(); | ||
| go("Mode.View")(); | ||
| }} | ||
| > | ||
| Save | ||
| </button> | ||
| <button | ||
| data-test={"cancel-mode-" + idx} | ||
| onClick={e => go("Mode.View", num)()} | ||
| > | ||
| Cancel | ||
| </button> | ||
| <button | ||
| data-test={"delete-" + idx} | ||
| onClick={e => { | ||
| const newData = data.filter((_, idx2) => idx !== idx2) | ||
| go("Items.List", newData)() | ||
| }} | ||
| > | ||
| Delete | ||
| </button> | ||
| </div> | ||
| ); | ||
| }} | ||
| /> | ||
| }} | ||
| /> | ||
| </Submachine> | ||
| ))} | ||
| </div> | ||
| </React.Fragment> | ||
| ); | ||
| }} | ||
| Hide={() => ( | ||
| <div> | ||
| <button onClick={go("Visibility.Show")}> | ||
| Toggle Show/Hide | ||
| </button> | ||
| </div> | ||
| )} | ||
| /> | ||
| )} | ||
| /> | ||
| </Machinate> | ||
| ); | ||
| } | ||
| } | ||
| ReactDOM.render( | ||
| // <Inspector />, | ||
| <Demo />, | ||
| document.getElementById("root") | ||
| ); | ||
| </script> | ||
| </body> | ||
| </html> |
| { | ||
| "name": "machinate-plugins-inspector", | ||
| "version": "1.0.2", | ||
| "description": "get full insight and power over your machinate-powered apps", | ||
| "main": "build/lib.js", | ||
| "author": "Pratik Ringshia", | ||
| "license": "MIT", | ||
| "scripts": { | ||
| "start": "NODE_ENV=development webpack --watch", | ||
| "test": "jest", | ||
| "build": "NODE_ENV=production webpack", | ||
| "prepublishOnly": "NODE_ENV=production webpack" | ||
| }, | ||
| "dependencies": { | ||
| "prop-types": "^15.6.1", | ||
| "react-frame-component": "^2.0.2" | ||
| }, | ||
| "peerDependencies": { | ||
| "machinate": "^1.0.12", | ||
| "react": "^16.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "babel-core": "^6.26.0", | ||
| "babel-eslint": "^7.2.3", | ||
| "babel-loader": "^7.1.2", | ||
| "babel-preset-env": "^1.6.1", | ||
| "babel-preset-react-app": "^3.1.1", | ||
| "eslint": "^4.18.0", | ||
| "eslint-config-react-app": "^2.1.0", | ||
| "eslint-plugin-flowtype": "^2.45.0", | ||
| "eslint-plugin-import": "^2.8.0", | ||
| "eslint-plugin-jsx-a11y": "^5.1.1", | ||
| "eslint-plugin-react": "^7.6.1", | ||
| "jest": "^22.3.0", | ||
| "machinate": "^1.0.12", | ||
| "prop-types": "^15.6.1", | ||
| "react": "^16.2.0", | ||
| "react-dom": "^16.2.0", | ||
| "webpack": "^3.11.0" | ||
| } | ||
| } |
| import React from "react"; | ||
| import Frame from "react-frame-component"; | ||
| import PropTypes from "prop-types"; | ||
| import ReactDOM from "react-dom"; | ||
| import { transitionsTransform } from "./transforms"; | ||
| const INSTRUMENT = false; | ||
| // const transitionsTransform = list => { | ||
| // const val = list.reduce( | ||
| // (info, item) => { | ||
| // if (info.last && item.toName === info.last.toName) { | ||
| // const lastItem = info.list[info.list.length - 1]; | ||
| // lastItem.subItems = [...lastItem.subItems, item]; | ||
| // } else { | ||
| // const newItem = { ...item, subItems: [] }; | ||
| // info.list.push(newItem); | ||
| // } | ||
| // return { last: item, list: info.list }; | ||
| // }, | ||
| // { last: null, list: [] } | ||
| // ); | ||
| // // console.log(val); | ||
| // return val; | ||
| // }; | ||
| function bindEvent(element, eventName, eventHandler) { | ||
| if (element.addEventListener) { | ||
| element.addEventListener(eventName, eventHandler, false); | ||
| } else if (element.attachEvent) { | ||
| element.attachEvent("on" + eventName, eventHandler); | ||
| } | ||
| } | ||
| class InspectorState extends React.Component { | ||
| instrument = INSTRUMENT; | ||
| state = { | ||
| appState: null, | ||
| transitionsList: [] | ||
| }; | ||
| constructor(props) { | ||
| super(props); | ||
| this.state.appState = props.initial; | ||
| } | ||
| handleMessage = message => { | ||
| if (message.source === "machinate-to-inspector") { | ||
| this.instrument && console.log("received from parent: ", message); | ||
| if (message.type === "state") { | ||
| this.setState({ appState: message.data }); | ||
| } | ||
| if (message.type === "transition") { | ||
| this.setState({ | ||
| transitionsList: [...this.state.transitionsList, message.data] | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| render() { | ||
| return ( | ||
| this.props.children && | ||
| this.props.children({ | ||
| ...this.state, | ||
| handleMessage: this.handleMessage.bind(this) | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| class MessageBroker extends React.Component { | ||
| postFromInspector = (type, data) => { | ||
| const window = this.window; | ||
| if (window) { | ||
| window.parent.postMessage( | ||
| { source: "machinate-from-inspector", type, data }, | ||
| "*" | ||
| ); | ||
| } | ||
| }; | ||
| componentDidMount() { | ||
| this.window = this.context && this.context.window; | ||
| // this.postFromInspector("Hello!"); | ||
| if (this.window) { | ||
| bindEvent(this.window, "message", e => { | ||
| this.props.onMessage(e.data); | ||
| }); | ||
| } | ||
| } | ||
| render() { | ||
| return ( | ||
| (this.props.children && | ||
| this.props.children({ post: this.postFromInspector })) || | ||
| null | ||
| ); | ||
| } | ||
| } | ||
| MessageBroker.contextTypes = { | ||
| window: PropTypes.any, | ||
| document: PropTypes.any | ||
| }; | ||
| class Inspector extends React.Component { | ||
| instrument = INSTRUMENT; | ||
| state = {}; | ||
| postToInspector = (type, data) => { | ||
| const frame = ReactDOM.findDOMNode(this.iframe); | ||
| frame.contentWindow.postMessage( | ||
| { source: "machinate-to-inspector", type, data }, | ||
| "*" | ||
| ); | ||
| }; | ||
| componentDidMount() { | ||
| const machine = this.context && this.context.machine; | ||
| if (window && machine) { | ||
| this.setState({ initialState: machine.getState() }); | ||
| // console.log(machine.getState()); | ||
| // this.postToInspector("init-state", machine.getState()); | ||
| machine.addListener("transition", (type, data) => | ||
| this.postToInspector("transition", data) | ||
| ); | ||
| machine.addListener("set-state", (type, data) => | ||
| this.postToInspector("state", data.state) | ||
| ); | ||
| machine.addListener("force-state", (type, data) => | ||
| this.postToInspector("state", data.state) | ||
| ); | ||
| machine.addListener("log", (type, data) => console.log(data)); | ||
| bindEvent(window, "message", e => { | ||
| if (e.data.source === "machinate-from-inspector") { | ||
| this.instrument && console.log("received from child", e.data); | ||
| if (e.data.type === "transition") { | ||
| machine.transition( | ||
| [], | ||
| ...e.data.data.name.split("."), | ||
| e.data.data.payload | ||
| ); | ||
| } else if (e.data.type === "force-state") { | ||
| machine.setState(e.data.data); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| render() { | ||
| return ( | ||
| <div | ||
| style={{ | ||
| position: "fixed", | ||
| top: "0", | ||
| right: "0", | ||
| height: "100vh", | ||
| width: "300px" // TODO: make resizable | ||
| }} | ||
| > | ||
| <Frame | ||
| ref={ref => { | ||
| if (ref) { | ||
| this.iframe = ref; | ||
| } | ||
| }} | ||
| style={{ border: "0", height: "100vh" }} | ||
| head={ | ||
| <style>{` | ||
| * { box-sizing: border-box; } | ||
| html, body {margin:0; padding: 0; font-family: Arial, serif; } | ||
| body { background-color: #efefef; color: #3f3f3f; border-left: 1px solid #aaaaaa; min-height: 100vh; } | ||
| .container { padding: 20px 15px; font-size: 12px; } | ||
| html { height: 100vh; } | ||
| h1 { font-size: 14px; margin: 0; padding: 10px 15px; background-color: #d0d0d0; color: #333; border-bottom: 1px solid #777; } | ||
| h1, h3 { text-transform: uppercase; font-size: 11px; letter-spacing: 1px; } | ||
| h3 { margin: 0 0 5px 0; padding: 0 0 4px 0; border-bottom: 1px solid #d0d0d0; } | ||
| .module { margin-bottom: 30px; } | ||
| .transition { margin-bottom: 3px; } | ||
| .pointer { cursor: pointer; } | ||
| `}</style> | ||
| } | ||
| > | ||
| <InspectorState initial={this.state.initialState}> | ||
| {({ appState, transitionsList, handleMessage }) => ( | ||
| <MessageBroker onMessage={handleMessage}> | ||
| {({ post }) => ( | ||
| <div> | ||
| <h1 style={{ marginTop: "0px" }}>Inspector</h1> | ||
| <div className="container"> | ||
| <div className="module"> | ||
| <h3>WATCH</h3> | ||
| </div> | ||
| <div className="module"> | ||
| <h3>STATE</h3> | ||
| <div style={{ height: "200px", overflowY: "scroll" }}> | ||
| {appState | ||
| ? Object.keys(appState).map(domain => ( | ||
| <div key={domain}> | ||
| {domain}: {appState[domain].state}{" "} | ||
| {JSON.stringify(appState[domain].data)} | ||
| </div> | ||
| )) | ||
| : "Undetected"} | ||
| </div> | ||
| </div> | ||
| <div className="module"> | ||
| <h3>TRANSITIONS</h3> | ||
| <div> | ||
| {transitionsTransform(transitionsList).list.map( | ||
| (t, idx) => | ||
| t.subItems.length >= 1 ? ( | ||
| <div className="transition" key={idx}> | ||
| {t.fromName} ➡ {t.toName}{" "} | ||
| {[t, ...t.subItems].map((sub, idx2) => ( | ||
| <em | ||
| className="pointer" | ||
| key={idx + "-" + idx2} | ||
| onClick={(state => () => | ||
| post("force-state", state))(sub.state)} | ||
| > | ||
| •{" "} | ||
| </em> | ||
| ))} | ||
| </div> | ||
| ) : ( | ||
| <div | ||
| className="transition pointer" | ||
| key={idx} | ||
| onClick={(state => () => | ||
| post("force-state", state))(t.state)} | ||
| > | ||
| {t.fromName} ➡ {t.toName}{" "} | ||
| <em>{t.payload ? "with payload" : ""}</em> | ||
| </div> | ||
| ) | ||
| )} | ||
| </div> | ||
| </div> | ||
| {/* <button | ||
| onClick={() => | ||
| post("transition", { | ||
| name: "Auth.LoggedIn", | ||
| payload: new Date() | ||
| }) | ||
| } | ||
| > | ||
| Transition | ||
| </button> */} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </MessageBroker> | ||
| )} | ||
| </InspectorState> | ||
| </Frame> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
| Inspector.contextTypes = { | ||
| machine: PropTypes.object | ||
| }; | ||
| export { Inspector }; |
| const transitionsTransform = list => { | ||
| const val = list.reduce( | ||
| (info, item) => { | ||
| if (info.last && item.toName === info.last.toName) { | ||
| const lastItem = info.list[info.list.length - 1]; | ||
| lastItem.subItems = [...lastItem.subItems, item]; | ||
| } else { | ||
| const newItem = { ...item, subItems: [] }; | ||
| info.list.push(newItem); | ||
| } | ||
| return { last: item, list: info.list }; | ||
| }, | ||
| { last: null, list: [] } | ||
| ); | ||
| // console.log(val); | ||
| return val; | ||
| }; | ||
| export { transitionsTransform }; |
| const path = require("path"); | ||
| const isProd = process.env.NODE_ENV === "production"; | ||
| module.exports = { | ||
| entry: "./src/index.js", | ||
| devtool: isProd ? "cheap-module-source-map" : "source-map", | ||
| output: { | ||
| path: path.resolve("build"), | ||
| filename: "lib.js", | ||
| library: "MachinateInspector", | ||
| libraryTarget: "umd" | ||
| }, | ||
| module: { | ||
| loaders: [ | ||
| { test: /\.js$/, loader: "babel-loader", exclude: /node_modules/ }, | ||
| { test: /\.jsx$/, loader: "babel-loader", exclude: /node_modules/ } | ||
| ] | ||
| } | ||
| }; |
| // I whipped up the below emitter, is it worth it to use a supported one? | ||
| // https://github.com/Olical/EventEmitter/blob/master/docs/guide.md | ||
| const emitter = function() { | ||
| let listeners = {}; | ||
| const addListener = (type, listener) => { | ||
| listeners[type] = | ||
| listeners[type] && Array.isArray(listeners[type]) | ||
| ? [...listeners[type], listener] | ||
| : [listener]; | ||
| }; | ||
| const removeListener = (type, listener) => { | ||
| listeners[type] = | ||
| listeners[type] && Array.isArray(listeners[type]) | ||
| ? listeners[type].filter(l => l !== listener) | ||
| : []; | ||
| }; | ||
| const emit = (type, data) => { | ||
| if (type instanceof RegExp) { | ||
| Object.keys(listeners) | ||
| .filter(key => type.test(key)) | ||
| .forEach(key => { | ||
| const list = listeners[key]; | ||
| if (list && Array.isArray(list)) { | ||
| list.forEach(l => l(type, data)); | ||
| } | ||
| }); | ||
| } else { | ||
| listeners[type] && | ||
| Array.isArray(listeners[type]) && | ||
| listeners[type].forEach(l => l(type, data)); | ||
| } | ||
| }; | ||
| return { | ||
| addListener, | ||
| removeListener, | ||
| emit | ||
| }; | ||
| }; | ||
| export default emitter; |
+17
-2
| sudo: false | ||
| language: node_js | ||
| node_js: 8 | ||
| install: npm install | ||
| script: npm test | ||
| cache: | ||
| directories: | ||
| - ~/.npm | ||
| - node_modules | ||
| before_install: | ||
| - npm install -g http-server | ||
| install: | ||
| - npm link && npm run build | ||
| - cd demo/03-submachines && npm install | ||
| - npm link machinate | ||
| - npm run build | ||
| before_script: | ||
| - http-server ./build -p 3001 & | ||
| - cd ../.. | ||
| script: | ||
| - npm test | ||
| - $(npm bin)/cypress run --record |
+5
-2
| { | ||
| "name": "machinate", | ||
| "version": "1.0.12", | ||
| "version": "1.0.13", | ||
| "description": "practical state management", | ||
@@ -10,3 +10,4 @@ "main": "build/lib.js", | ||
| "build": "NODE_ENV=production webpack", | ||
| "prepublishOnly": "NODE_ENV=production webpack" | ||
| "prepublishOnly": "NODE_ENV=production webpack", | ||
| "cypress": "cypress" | ||
| }, | ||
@@ -42,2 +43,3 @@ "repository": { | ||
| "babel-preset-react-app": "^3.1.1", | ||
| "cypress": "^2.0.3", | ||
| "enzyme": "^3.3.0", | ||
@@ -47,2 +49,3 @@ "enzyme-adapter-react-16": "^1.1.1", | ||
| "eslint-config-react-app": "^2.1.0", | ||
| "eslint-plugin-cypress": "^2.0.1", | ||
| "eslint-plugin-flowtype": "^2.45.0", | ||
@@ -49,0 +52,0 @@ "eslint-plugin-import": "^2.8.0", |
@@ -11,2 +11,7 @@ import { Component } from "react"; | ||
| componentWillUnmount() { | ||
| // console.log( | ||
| // "Actual state unmounting", | ||
| // this.context.scope.join("/"), | ||
| // this.props._config.domainName | ||
| // ); | ||
| this.props._config.onRemove(this); | ||
@@ -13,0 +18,0 @@ } |
@@ -15,6 +15,22 @@ import React from "react"; | ||
| machine.onSetState(() => this.forceUpdate()); | ||
| machine.addListener("force-state", () => { | ||
| this.forced = true; | ||
| this.forceUpdate(); | ||
| }); | ||
| this.state = { machine }; | ||
| } | ||
| componentDidUpdate() { | ||
| if (this.forced) { | ||
| this.forced = false; | ||
| this.forceUpdate(); // update context value | ||
| } | ||
| } | ||
| getChildContext() { | ||
| return { | ||
| machine: this.state.machine, | ||
| scope: [], | ||
| forced: !!this.forced | ||
| }; | ||
| } | ||
| createMachine(scheme, initialState) { | ||
@@ -27,8 +43,2 @@ // TODO: Implement | ||
| } | ||
| getChildContext() { | ||
| return { | ||
| machine: this.state.machine, | ||
| scope: [] | ||
| }; | ||
| } | ||
| } | ||
@@ -43,3 +53,4 @@ | ||
| machine: PropTypes.object, | ||
| scope: PropTypes.array | ||
| scope: PropTypes.array, | ||
| forced: PropTypes.bool | ||
| }; | ||
@@ -46,0 +57,0 @@ |
| import React from "react"; | ||
| import PropTypes from "prop-types"; | ||
| const States = ({ for: forDomain, ...props }, { machine, scope }) => { | ||
| const DomainState = machine.componentForDomain(scope, forDomain); | ||
| return <DomainState {...props} />; | ||
| }; | ||
| class States extends React.Component { | ||
| // shouldComponentUpdate() { | ||
| // console.log("Should update?", this.context.scope.join("/"), this.props.for); | ||
| // return true; | ||
| // } | ||
| // componentWillUnmount() { | ||
| // console.log( | ||
| // "States unmounting", | ||
| // this.context.scope.join("/"), | ||
| // this.props.for | ||
| // ); | ||
| // } | ||
| componentWillMount() { | ||
| const { for: forDomain } = this.props; | ||
| const { machine, scope } = this.context; | ||
| this.DomainState = machine.componentForDomain(scope, forDomain); | ||
| } | ||
| render() { | ||
| const { for: forDomain, ...props } = this.props; | ||
| const { DomainState } = this; | ||
| return <DomainState key={forDomain} {...props} />; | ||
| } | ||
| } | ||
| States.propTypes = { | ||
@@ -10,0 +31,0 @@ for: PropTypes.string.isRequired |
@@ -14,7 +14,8 @@ import { Component } from "react"; | ||
| context.machine.registerSubmachine( | ||
| this.getChildContext().scope, | ||
| props.initial | ||
| ); | ||
| this.register(context.machine, props, context); | ||
| } | ||
| register(machine, props, context) { | ||
| !context.forced && | ||
| machine.registerSubmachine(this.getChildContext().scope, props.initial); | ||
| } | ||
| componentDidMount() { | ||
@@ -30,7 +31,13 @@ // TODO: Test out this behavior of re-registering submachines in cDM. | ||
| // the parent submachine's state. | ||
| this.context.machine.registerSubmachine( | ||
| this.getChildContext().scope, | ||
| this.props.initial | ||
| ); | ||
| // | ||
| // this.register(this.context.machine); | ||
| } | ||
| componentWillReceiveProps(nextProps, nextContext) { | ||
| // TODO: implement a better deep compare | ||
| const deepdiff = (a, b) => JSON.stringify(a) !== JSON.stringify(b); | ||
| if (deepdiff(this.props.initial, nextProps.initial)) { | ||
| // console.log("re-registering ", nextProps.id, "with", nextProps.initial); | ||
| this.register(this.context.machine, nextProps, nextContext); | ||
| } | ||
| } | ||
| componentWillUnmount() { | ||
@@ -46,3 +53,4 @@ this.context.machine.removeSubmachine(this.getChildContext().scope); | ||
| scope: PropTypes.array, | ||
| machine: PropTypes.object | ||
| machine: PropTypes.object, | ||
| forced: PropTypes.bool | ||
| }; | ||
@@ -49,0 +57,0 @@ |
+132
-65
@@ -12,12 +12,9 @@ import React from "react"; | ||
| import DomainState from "./components/DomainState"; | ||
| import eventemitter from "./emitter"; | ||
| const createMachine = function(schema, state, parentMachine) { | ||
| const createMachine = function(schema, state) { | ||
| let components = []; | ||
| const rootMachine = parentMachine | ||
| ? parentMachine.rootMachine || parentMachine | ||
| : null; | ||
| const submachines = []; | ||
| let onSetState = () => null; | ||
| const emitter = eventemitter(); | ||
| // TODO: throw if scheme or initial state is missing | ||
| if (!schema) { | ||
@@ -30,2 +27,4 @@ throw new Error("Machine must be initialized with a scheme"); | ||
| emitter.emit("init-state", { state }); | ||
| const depGraph = createGraph(schema); | ||
@@ -46,10 +45,19 @@ | ||
| const _setState = nextState => { | ||
| state = nextState; | ||
| emitter.emit("set-state", { state }); | ||
| }; | ||
| const setState = nextState => { | ||
| state = nextState; | ||
| onSetState(); | ||
| emitter.emit("force-state", { state }); | ||
| }; | ||
| const transition = (scope, domainName, stateName, payload) => { | ||
| // TODO: resolve these names using scope: | ||
| // console.log(scope, domainName, stateName, payload); | ||
| const transition = ( | ||
| scope, | ||
| domainName, | ||
| stateName, | ||
| payload, | ||
| isTriggered = false | ||
| ) => { | ||
| const { | ||
@@ -60,2 +68,3 @@ prefix, | ||
| } = resolveSubdomain(state, scope, domainName); | ||
| const schematicToName = | ||
@@ -68,5 +77,4 @@ domainName.split("/").slice(-1)[0] + "." + stateName; | ||
| resolvedDomainName + "." + state[resolvedDomainName].state; | ||
| const schematicFromName = fromName && fromName.split("/").slice(-1)[0]; | ||
| const unresolvedFromName = fromName && fromName.split("/").slice(-1)[0]; | ||
| if ( | ||
@@ -100,50 +108,101 @@ !isTransitionable( | ||
| state[resolvedDomainName] = { | ||
| state: stateName, | ||
| data: payload | ||
| }; | ||
| log( | ||
| [ | ||
| "going from", | ||
| fromName, | ||
| "to", | ||
| toName, | ||
| "with payload", | ||
| payload, | ||
| "and dependents", | ||
| depGraph.getDependents(state, fromName).map(n => n.f), | ||
| depGraph.getDependents(state, toName).map(n => n.f) | ||
| ].join(" ") | ||
| ); | ||
| const domainsToRemove = depGraph | ||
| .getDependents(state, unresolvedFromName) | ||
| .map(n => n.f) | ||
| .map(domain => prefix + domain); | ||
| const oldState = state[resolvedDomainName]; | ||
| state = Object.entries(state).reduce((obj, [key, value]) => { | ||
| if (!domainsToRemove.includes(key)) { | ||
| obj[key] = value; | ||
| _setState({ | ||
| ...state, | ||
| ...{ | ||
| [resolvedDomainName]: { | ||
| state: stateName, | ||
| data: payload | ||
| } | ||
| } | ||
| return obj; | ||
| }, {}); | ||
| }); | ||
| depGraph.getDependents(state, schematicToName).forEach(node => { | ||
| if (node.ts[0].name) { | ||
| transition(prefixArray, node.f, node.ts[0].name); | ||
| const dependentDomainsToAdd = depGraph | ||
| .getDependents(state, schematicToName) | ||
| .map(node => ({ domain: node.f, state: node.ts[0] && node.ts[0].name })); | ||
| const dependentDomainsToRemove = depGraph | ||
| .getDependents(state, schematicFromName) | ||
| .map(n => n.f); | ||
| const optimizedDomainsToRemove = dependentDomainsToRemove | ||
| .filter(domain => { | ||
| const overlap = dependentDomainsToAdd.find(x => x.domain === domain); | ||
| if ( | ||
| overlap && | ||
| state[prefix + domain] && | ||
| overlap.state === state[prefix + domain].state | ||
| ) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }) | ||
| .map(domain => prefix + domain); | ||
| const optimizedDomainsToAdd = dependentDomainsToAdd.filter(toAdd => { | ||
| const overlap = dependentDomainsToRemove.find( | ||
| domain => domain === toAdd.domain | ||
| ); | ||
| if (overlap && overlap === toAdd.domain) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| // should dependency components be included in this force update as well? | ||
| const comps = components.filter(comp => { | ||
| const fullName = [ | ||
| ...comp.props._config.scope, | ||
| comp.props._config.domainName | ||
| ].join("/"); | ||
| return fullName === resolvedDomainName; | ||
| optimizedDomainsToAdd.forEach(toAdd => { | ||
| console.log("triggering " + toAdd.domain + "." + toAdd.state); | ||
| // ponder: we are recursing before updating the state below, is that ok? | ||
| emitter.emit("triggered-add", { ...toAdd }); | ||
| transition(prefixArray, toAdd.domain, toAdd.state, undefined, true); | ||
| }); | ||
| comps.forEach(comp => | ||
| console.log( | ||
| "Updating", | ||
| [...comp.props._config.scope, comp.props._config.domainName].join("/") | ||
| ) | ||
| _setState( | ||
| Object.entries(state).reduce((obj, [key, value]) => { | ||
| if (!optimizedDomainsToRemove.includes(key)) { | ||
| obj[key] = value; | ||
| } | ||
| emitter.emit("triggered-remove", { domain: key }); | ||
| return obj; | ||
| }, {}) | ||
| ); | ||
| comps.forEach(comp => comp.forceUpdate()); | ||
| // _updateAll(); | ||
| }; | ||
| // should dependency components be included in this force update as well? | ||
| if (schematicToName !== schematicFromName || payload !== oldState.data) { | ||
| const comps = components.filter(comp => { | ||
| const fullName = _componentFullName(comp); | ||
| return fullName === resolvedDomainName; | ||
| }); | ||
| comps.forEach(comp => comp.forceUpdate()); | ||
| } | ||
| const _updateAll = () => { | ||
| // - is there a better, more efficient way to do this? | ||
| // - is this idiomatic react? should I just setState() on sub-components? | ||
| components.forEach(comp => comp.forceUpdate()); | ||
| if (isTriggered) { | ||
| emitter.emit("transition-triggered", { | ||
| fromName, | ||
| toName, | ||
| payload, | ||
| state | ||
| }); | ||
| } else { | ||
| emitter.emit("transition", { fromName, toName, payload, state }); | ||
| } | ||
| }; | ||
| const _componentFullName = comp => | ||
| [...comp.props._config.scope, comp.props._config.domainName].join("/"); | ||
| const go = (scope, notation, payload) => _ => { | ||
@@ -155,5 +214,2 @@ const [domainName, stateName] = notation.split("."); | ||
| const componentForDomain = (scope, domainName) => { | ||
| // const resolvedDomain = [...scope, domainName].join("/"); | ||
| // console.log("name", resolvedDomain, getState()[resolvedDomain]); | ||
| const generatedPropTypes = Object.values( | ||
@@ -177,2 +233,3 @@ getDomainInfo(domainName).states || {} | ||
| go: (...args) => go(scope, ...args), | ||
| log: log, | ||
| getState | ||
@@ -198,3 +255,12 @@ } | ||
| }, {}); | ||
| state = Object.assign({}, state, prefixedInitialState); | ||
| _setState(Object.assign({}, state, prefixedInitialState)); | ||
| // Perhaps updates should be moved into _setState instead. Fix bug where after | ||
| // a submachine was reregistered with a different initial value, the component | ||
| // wasn't updating in the UI. | ||
| // const componentsToUpdate = components.filter(comp => | ||
| // _componentFullName(comp).startsWith([...scope, ""].join("/")) | ||
| // ); | ||
| // componentsToUpdate.forEach(comp => comp.forceUpdate()); | ||
| }; | ||
@@ -204,10 +270,14 @@ | ||
| const prefix = scope.join("/") + "/"; | ||
| state = Object.entries(state).reduce((obj, [key, value]) => { | ||
| if (!key.startsWith(prefix)) { | ||
| obj[key] = value; | ||
| } | ||
| return obj; | ||
| }, {}); | ||
| _setState( | ||
| Object.entries(state).reduce((obj, [key, value]) => { | ||
| if (!key.startsWith(prefix)) { | ||
| obj[key] = value; | ||
| } | ||
| return obj; | ||
| }, {}) | ||
| ); | ||
| }; | ||
| const log = msg => emitter.emit("log", msg); | ||
| const createdMachine = { | ||
@@ -218,17 +288,14 @@ getState, | ||
| onSetState: fn => (onSetState = fn), | ||
| getDomainInfo, | ||
| componentForDomain, | ||
| getComponents: () => components, | ||
| rootMachine, | ||
| registerSubmachine, | ||
| removeSubmachine, | ||
| getSubmachines: () => submachines | ||
| getSubmachines: () => submachines, | ||
| addListener: emitter.addListener, | ||
| removeListener: emitter.removeListener, | ||
| log | ||
| }; | ||
| if (parentMachine) { | ||
| rootMachine.registerSubmachine(createdMachine); | ||
| } | ||
| return createdMachine; | ||
@@ -235,0 +302,0 @@ }; |
@@ -85,3 +85,2 @@ import React from "react"; | ||
| Visible={(_, { transition }) => { | ||
| console.log("yo"); | ||
| transition("Display", "Hidden"); | ||
@@ -88,0 +87,0 @@ }} |
@@ -54,3 +54,3 @@ import { createMachine, isTransitionable } from "../src/machine"; | ||
| test("should trigger new dependent states", () => { | ||
| const initialState = { Auth: "LoggedIn" }; | ||
| const initialState = { Auth: "LoggedOut" }; | ||
@@ -64,3 +64,3 @@ const machine = createMachine(scheme, initialState); | ||
| test("should destroy old dependent states", () => { | ||
| const initialState = { Auth: "LoggedIn" }; | ||
| const initialState = { Auth: "LoggedOut" }; | ||
@@ -67,0 +67,0 @@ const machine = createMachine(scheme, initialState); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
623182
54.06%39
69.57%8772
28.81%0
-100%18
12.5%41
24.24%