Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VYZN: Matrix Messaging Integration #455

Merged
merged 25 commits into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3d43860
install matrix-widget-api
clecherbauer Oct 21, 2022
798f50f
bldrs to vyzn integration wip
clecherbauer Oct 20, 2022
37a733a
test out routing
clecherbauer Oct 21, 2022
5a68252
refactor code, use store instead of router
clecherbauer Oct 25, 2022
069a99c
changes for codeReview
clecherbauer Nov 5, 2022
4206e78
move WidgetApi into react context, use navigation() instead of the de…
clecherbauer Nov 8, 2022
301b66c
seperate events for selection an unselection of elements
clecherbauer Nov 11, 2022
52f3f1d
select multiple elements
clecherbauer Nov 12, 2022
bcb98a2
remove code comments
clecherbauer Nov 18, 2022
a376e72
changes from codereview
clecherbauer Nov 29, 2022
5ca7f6a
VYZN-806: Tests for Bldrs integration
Dec 16, 2022
2a7e985
Merge pull request #1 from Adrian62D/bldrs-to-vyzn
clecherbauer Dec 19, 2022
f44de2f
Merge remote-tracking branch 'origin/main' into bldrs-to-vyzn
clecherbauer Dec 21, 2022
9053e4d
fix issue after merge
clecherbauer Jan 4, 2023
51c9081
fix new linting issues after merge
clecherbauer Jan 4, 2023
2d7af8f
Merge remote-tracking branch 'origin/main' into bldrs-to-vyzn
clecherbauer Jan 4, 2023
a8ed57d
trigger preview deployment
clecherbauer Jan 6, 2023
adc8cf1
Merge remote-tracking branch 'bldrs/main' into bldrs-to-vyzn
Jan 18, 2023
b8e2267
stabilize cypress integration tests
Jan 19, 2023
c90c50f
cadview test fix
Jan 19, 2023
de92e48
Merge pull request #2 from aozien/bldrs-to-vyzn
clecherbauer Jan 19, 2023
b29368f
fix yarn lock conflict
Jan 20, 2023
a5c8c80
parameters bug fix & adding test case for it
Jan 20, 2023
dd022db
added matrix-widget-api to the lock file
Jan 21, 2023
55ec16a
Merge branch 'main' into bldrs-to-vyzn
pablo-mayrgundter Jan 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# misc
.DS_Store
.vscode
.idea
*.log

cypress/videos
Expand Down
21 changes: 13 additions & 8 deletions __mocks__/web-ifc-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ const loadedModel = {
getProperties: jest.fn((eltId) => ({})),
},
getIfcType: jest.fn(),
geometry: {
boundingBox: {
getCenter: jest.fn(),
},
},
}


Expand All @@ -21,22 +26,22 @@ const impl = {
context: {
ifcCamera: {
cameraControls: {
setPosition: (x, y, z) => {
setPosition: jest.fn((x, y, z) => {
return {}
},
getPosition: (x, y, z) => {
}),
getPosition: jest.fn((x, y, z) => {
// eslint-disable-next-line no-magic-numbers
const position = [0, 0, 0]
return position
},
setTarget: (x, y, z) => {
}),
setTarget: jest.fn((x, y, z) => {
return {}
},
getTarget: (x, y, z) => {
}),
getTarget: jest.fn((x, y, z) => {
// eslint-disable-next-line no-magic-numbers
const target = [0, 0, 0]
return target
},
}),
},
},
},
Expand Down
102 changes: 102 additions & 0 deletions cypress/e2e/integration/bldrs-inside-iframe.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const path = require('path')

/**
* Black-box integration tests for Bldrs running in an iframe.
* Bldrs emits messages and receives messages via Matrix Widgets API.
*
* The setup includes a standalone web page with a Bldrs iframe and wired
* up message handling. This scenario comes as close as possible to a real-
* world integration scenario. An important difference is that the cypress
* framework itself loads the system under test within an iframe. This means
* that in all these tests Bldrs runs in an iframe which runs in an iframe.
*/
describe('bldrs inside iframe', () => {
const SYSTEM_UNDER_TEST = '/cypress/static/bldrs-inside-iframe.html'
const KEYCODE_ESC = 27

/**
* Copy web page to target directory to make it accessible to cypress.
*/
before(() => {
const fixtures = ['bldrs-inside-iframe.html', 'bldrs-inside-iframe.js']
const targetDirectory = 'docs/cypress/static/'
for (const fixture of fixtures) {
cy.fixture(fixture, null).then((content) => {
const outPath = path.join(targetDirectory, fixture)
cy.writeFile(outPath, content)
})
}
})

beforeEach(() => {
cy.clearCookies()
cy.visit(SYSTEM_UNDER_TEST)
cy.get('iframe').iframe().as('iframe')
cy.get('@iframe').trigger('keydown', {keyCode: KEYCODE_ESC})
})

it('should emit ready-messsage when page load completes', () => {
cy.get('#cbxIsReady').should('exist').and('be.checked')
})

it('should load model when LoadModel-message emitted', () => {
const model = 'Swiss-Property-AG/Momentum-Public/main/Momentum.ifc'
const modelRootNodeName = 'Momentum / KNIK v3'
cy.get('#txtSendMessageType').clear().type('ai.bldrs-share.LoadModel')
const msg = {
githubIfcPath: model,
}
cy.get('#txtSendMessagePayload').clear().type(JSON.stringify(msg), {parseSpecialCharSequences: false})
cy.get('#btnSendMessage').click()
cy.get('@iframe').contains('span', modelRootNodeName).should('exist')
})

it('should select element when SelectElements-message emitted', () => {
const globalId = '02uD5Qe8H3mek2PYnMWHk1'
const expectedExpressId = '621'
cy.get('#txtSendMessageType').clear().type('ai.bldrs-share.SelectElements')
const msg = {
globalIds: [globalId],
}
cy.get('#txtSendMessagePayload').clear().type(JSON.stringify(msg), {parseSpecialCharSequences: false})
cy.get('@iframe').find('[data-model-ready="true"]').should('exist')
cy.get('#btnSendMessage').click()
cy.get('@iframe').findByRole('button', {name: /Properties/}).click()
// Bldrs itemProperties dialog appears to slice the ID across different rows
// therefore we currently need to do it this way as a workaround:
cy.get('iframe').iframe().contains('span', expectedExpressId[0]).should('exist')
cy.get('iframe').iframe().contains('span', expectedExpressId[1]).should('exist')
cy.get('iframe').iframe().contains('span', expectedExpressId[2]).should('exist')
})

it('should emit ElementsSelected-message when element was selected from panel', () => {
cy.get('@iframe').find('[data-model-ready="true"]').should('exist')
cy.get('@iframe').findByText(/bldrs/i).click()
cy.get('@iframe').findByText(/build/i).click()

cy.get('#txtLastMsg').should(($txtLastMsg) => {
const msg = JSON.parse($txtLastMsg.val())
assert.equal(msg.api, 'fromWidget')
assert.equal(msg.widgetId, 'bldrs-share')
assert.exists(msg.requestId)
assert.equal(msg.action, 'ai.bldrs-share.ElementsSelected')
assert.exists(msg.data)
})
})

it('should emit ElementsDeSelected-message when selection was cleared', () => {
cy.get('@iframe').find('[data-model-ready="true"]').should('exist')
cy.get('@iframe').findByText(/bldrs/i).click()
cy.get('@iframe').findByText(/build/i).click()

cy.get('@iframe').findByRole('button', {name: /Clear/}).click()
cy.get('#txtLastMsg').should(($txtLastMsg) => {
const msg = JSON.parse($txtLastMsg.val())
assert.equal(msg.api, 'fromWidget')
assert.equal(msg.widgetId, 'bldrs-share')
assert.exists(msg.requestId)
assert.equal(msg.action, 'ai.bldrs-share.ElementsDeSelected')
assert.exists(msg.data)
})
})
})
43 changes: 43 additions & 0 deletions cypress/fixtures/bldrs-inside-iframe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html style="overflow:hidden;height:100%">
<head>
<meta charset="utf-8">
<title>Bldrs running inside an iframe</title>
<script type="text/javascript" src="https://unpkg.com/matrix-widget-api@1.1.1/dist/api.js"></script>

<!-- initializes & wires up all messages -->
<script type="text/javascript" src="bldrs-inside-iframe.js"></script>
</head>

<!--
This page contains a) a Bldrs instance running in an iframe and b) preset controls to emit messages and receive messages to/from Bldrs.
This serves as a basic foundation for the black-box integration tests located at /cypress/e2e/integration/
-->
<body style="overflow:hidden;height:100%">

<!-- Flag is set once Matrix Widget API is ready -->
<input id="cbxIsReady" type="checkbox"></input>
<label for="cbxIsReady">Is Ready</label>
<br/>

<!-- Contains the last message received from the iframe through Matrix Widget API -->
<input id="txtLastMsg" type="text"></input>
<label for="txtLastMsg">Last Message Received</label>
<br/>

<!-- Sends a message to the iframe through Matrix Widget API -->
<input id="txtSendMessageType" type="text" placeholder="message type"></input>
<input id="txtSendMessagePayload" type="text" placeholder="message payload"></input>
<input id="btnSendMessage" type="button" value="Send"></input>

<!-- Contains Bldrs in an iframe, the URL is set later -->
<iframe
id="bldrs-widget-iframe"
title="bldrs"
style="overflow:hidden;height:90%;width:90%"
width="90%"
height="90%">
</iframe>

</body>
</html>
112 changes: 112 additions & 0 deletions cypress/fixtures/bldrs-inside-iframe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* The Bldrs widget.
*/
class BldrsWidget {
creatorUserId = 'ai.bldrs-share';
id = 'bldrs-share';
type ='m.custom';
url = null;
waitForIframeLoad = false;
}

/**
* The Bldrs Widget Driver.
*/
class BldrsWidgetDriver {
askOpenID(observer) {
// not implemented yet
}

getTurnServers() {
return undefined;
}

navigate(uri){
return Promise.resolve(undefined);
}

// NOSONAR
readEventRelations(
eventId,
roomId,
relationType,
eventType,
from,
to,
limit,
direction
) {
return Promise.resolve(undefined);
}

readRoomEvents(eventType, msgtype, limit, roomIds) {
return Promise.resolve([]);
}

readStateEvents(eventType, stateKey, limit, roomIds) {
return Promise.resolve([]);
}

sendEvent(eventType, content, stateKey, roomId) {
return Promise.resolve(undefined);
}

sendToDevice(eventType, encrypted, contentMap) {
return Promise.resolve(undefined);
}

validateCapabilities(requested) {
return Promise.resolve(requested);
}
}

/**
* Message types.
*/
const EVENT_VIEWER_LOAD_MODEL = 'ai.bldrs-share.LoadModel';
const EVENT_VIEWER_SELECT_ELEMENTS = 'ai.bldrs-share.SelectElements';
const EVENT_CLIENT_SELECT_ELEMENTS = 'ai.bldrs-share.ElementsSelected';
const EVENT_CLIENT_DESELECT_ELEMENTS = 'ai.bldrs-share.ElementsDeSelected';

document.addEventListener("DOMContentLoaded", function(event) {
const container = document.getElementById('bldrs-widget-iframe');
const bldrsWidget = new BldrsWidget();
bldrsWidget.url = location.protocol + '//' + location.host;
const widget = new mxwidgets.Widget(bldrsWidget);
const driver = new BldrsWidgetDriver();
const api = new mxwidgets.ClientWidgetApi(widget, container, driver);

const cbxIsReady = document.getElementById('cbxIsReady');
const txtLastMsg = document.getElementById('txtLastMsg');
const txtSendMessageType = document.getElementById('txtSendMessageType');
const txtSendMessagePayload = document.getElementById('txtSendMessagePayload');
const btnSendMessage = document.getElementById('btnSendMessage');

container.src = bldrsWidget.url;

api.on('ready', () => {
cbxIsReady.checked = true;
console.log("message: ready");
});

api.on('action:' + EVENT_CLIENT_SELECT_ELEMENTS, (event) => {
event.preventDefault();
txtLastMsg.value = JSON.stringify(event.detail)
api.transport.reply(event.detail, {});
});

api.on('action:' + EVENT_CLIENT_DESELECT_ELEMENTS, (event) => {
event.preventDefault();
txtLastMsg.value = JSON.stringify(event.detail)
api.transport.reply(event.detail, {});
});

btnSendMessage.addEventListener('click', () => {
const messageType = txtSendMessageType.value
const messagePayload = JSON.parse(txtSendMessagePayload.value)
api.transport.send(messageType, messagePayload);
})

});


13 changes: 13 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,16 @@
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
import '@testing-library/cypress/add-commands'

/**
* Allow access to elements inside iframe and chain commands from there.
* @source https://www.nicknish.co/blog/cypress-targeting-elements-inside-iframes
*/
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe, callback = () => {}) => {
return cy
.wrap($iframe)
.should(iframe => expect(iframe.contents().find('body')).to.exist)
.then(iframe => cy.wrap(iframe.contents().find('body')))
.within({}, callback)
})

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@sentry/react": "^7.31.1",
"@sentry/tracing": "^7.31.1",
"clsx": "^1.2.1",
"matrix-widget-api": "^1.1.1",
"normalize.css": "^8.0.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
Expand Down