What is JavaScript and what does it do? Before you start learning something new, it’s important to understand exactly what it is and what it does. This is especially useful when it comes to mastering a new programming language. In simple terms, JavaScript is a programming language used to make websites interactive. If you think about the basic makeup of a website, you have HTML, which describes and defines the basic content and structure of the website, then you have CSS, which tells the browser how this HTML content should be displayed—determining things like color and font. With just HTML and CSS, you have a website that looks good but doesn’t actually do much. JavaScript brings the website to life by adding functionality. JavaScript is responsible for elements that the user can interact with, such as drop-down menus, modal windows, and contact forms. It is also used to create things like animations, video players, and interactive maps. Nowadays, JavaScript is an all-purpose programming language—meaning it runs across the entire software stack. The most popular application of JavaScript is on the client side (aka frontend), but since Node.js came on the scene, many people run JavaScript on the server side (aka backend) as well. When used on the client side, JavaScript code is read, interpreted, and executed in the user’s web browser. When used on the server side, it is run on a remote computer. You can learn more about the difference between frontend and backend programming here. JavaScript isn’t only used to create websites. It can also be used to build browser-based games and, with the help of certain frameworks, mobile apps for different operating systems. The creation of new libraries and frameworks is also making it possible to build backend programs with JavaScript, such as web apps and server apps. Is it still worth learning JavaScript in 2021? The world of web development is constantly moving. With so many new tools popping up all the time, it can be extremely difficult to know where you should focus your efforts. As an aspiring developer, you’ll want to make sure that what you’re learning is still relevant in today’s industry. If you’re having doubts about JavaScript, it’s important to know that, since its creation in 1995, JavaScript is pretty much everywhere on the web—and that’s not likely to change any time soon. According to the 2020 StackOverflow developer survey, JavaScript is the most commonly used programming language for the eighth year in a row. It is currently used by 94.5% of all websites and, despite originally being designed as a client-side language, JavaScript has now made its way to the server-side of websites (thanks to Node.js), mobile devices (thanks to React Native and Ionic) and desktop (courtesy of Electron). As long as people are interacting with the web, you can assume that JavaScript is highly relevant—there’s no doubt that this is a language worth knowing! With that in mind, let’s look at some of the key benefits of becoming a JavaScript expert. Why learn JavaScript? The most obvious reason for learning JavaScript is if you have hopes of becoming a web developer. Even if you haven’t got your heart set on a tech career, being proficient in JavaScript will enable you to build websites from scratch—a pretty useful skill to have in today’s job market! If you do want to become a web developer, here are some of the main reasons why you should learn JavaScript: JavaScript experts are versatile JavaScript is an extremely versatile language. Once you’ve mastered it, the possibilities are endless: you can code on the client-side (frontend) using Angular and on the server-side (backend) using Node.js. You can also develop web, mobile, and desktop apps using React, React Native, and Electron, and you can even get involved in machine learning. If you want to become a frontend developer, JavaScript is a prerequisite. However, that’s not the only career path open to you as a JavaScript expert. Mastering this key programming language could see you go on to work in full-stack development, games development, information security software engineering, machine learning, and artificial intelligence—to name just a few! Ultimately, if you want any kind of development or engineering career, proficiency in JavaScript is a must. JavaScript experts are in-demand (and well-paid) JavaScript is the most popular programming language in the world, so it’s no wonder that JavaScript is one of the most sought-after skills in the web development industry today. According to the Devskiller IT Skills and Hiring Report 2020, 72% of companies are looking to hire JavaScript experts. Enter the search term “JavaScript” on job site Indeed and you’ll find over 40,000 jobs requiring this skill (in the US). Run the same search on LinkedIn and the results are in excess of 125,000. At the same time, the global demand for JavaScript seems to outweigh the expertise available on the market. According to this 2018 HackerRank report, 48% of employers worldwide need developers with JavaScript skills, while only 42% of student developers claim to be proficient in JavaScript. And, in their most recent report for 2020, HackerRank once again reports that JavaScript is the most popular language that hiring mangers look for in a web developer candidate. Not only are JavaScript experts in demand—they are also well-paid. In the United States, JavaScript developers earn an average yearly salary of $111,953 per year. We’ve covered this topic in more detail in our JavaScript salary guide, but as you can see, learning JavaScript can really boost your earning potential as a developer. JavaScript is beginner-friendly Compared to many other programming languages, JavaScript offers one of the more beginner-friendly entry points into the world of coding. The great thing about JavaScript is that it comes installed on every modern web browser—there’s no need to set up any kind of development environment, which means you can start coding with JavaScript right away! Another advantage of learning JavaScript as your first programming language is that you get instant feedback; with a minimal amount of JavaScript code, you’ll immediately see visible results. There’s also a huge JavaScript community on sites like Stack Overflow, so you’ll find plenty of support as you learn. Not only is JavaScript beginner-friendly; it will also set you up with some extremely valuable transferable skills. JavaScript supports object-oriented, functional, and imperative styles of programming—skills which can be transferred to any new language you might learn later on, such as Python, Java, or C++. JavaScript provides a crucial introduction to key principles and practices that you’ll take with you throughout your career as a developer. Should you learn plain JavaScript first or can you skip to frameworks and libraries? When deciding whether or not to learn JavaScript, what you’re really asking is whether or not you should learn “vanilla” JavaScript. Vanilla JavaScript just means plain JavaScript without any libraries or frameworks. Let’s explore what this means in more detail now. What is meant by vanilla JavaScript, libraries, and frameworks? If you research the term “vanilla JavaScript”, you might run into some confusion; however, all you need to know is that vanilla JavaScript is used to refer to native, standards-based, non-extended JavaScript. There is no difference between vanilla JavaScript and JavaScript—it’s just there to emphasize the usage of plain JavaScript without the use of libraries and frameworks. So what are libraries and frameworks? JavaScript libraries and frameworks both contain sets of prewritten, ready-to-use JavaScript code—but they’re not the same thing. You can think of a framework as your blueprint for building a website: it gives you a structure to work from, and contains ready-made components and tools that help you to build certain elements much quicker than if you were to code them from scratch. Some popular JavaScript frameworks include Angular, React, Vue, and Node.js. Frameworks also contain libraries. Libraries are smaller than frameworks, and tend to be used for more specific cases. A JavaScript library contains sets of JavaScript code which can be called upon to implement certain functions and features. Let’s imagine you want to code a particular element into your website. You could write, say, ten lines of JavaScript from scratch—or you could take the condensed, ready-made version from your chosen JavaScript library. Some examples of JavaScript libraries include jQuery, Lodash, and Underscore. The easiest way to understand how frameworks and libraries work together is to imagine you are building a house. The framework provides the foundation and the structure, while the library enables you to add in ready-made components (like furniture) rather than building your own from scratch. You can learn more about the relationship between languages and libraries in this post explaining the main differences between JavaScript and jQuery. For now, let’s go back to our original question: How important is it to learn vanilla JavaScript? Should you learn vanilla JavaScript first? When it comes to learning JavaScript, it can be tempting to skip ahead to those time-saving frameworks and libraries we just talked about—and many developers do. However, there are many compelling arguments for learning plain JavaScript first. While JavaScript frameworks may help you get the job done quicker, there’s only so far you can go if you don’t understand the core concepts behind these frameworks. Frontend developer Abhishek Nagekar describes how not learning vanilla JavaScript came back to bite him when he started learning the JavaScript frameworks Node and Express: “As I went to write more and more code in Node and Express, I began to get stuck at even the tiniest problems. Suddenly, I was surrounded with words like callbacks, closures, event loop and prototype. It felt like I got a reintroduction to JavaScript, but this time, it was not a toddler playing in its cradle, it was something of a mysterious monster, challenging me on every other step for not having taken it seriously.” The above Tweet references a long-running joke within the developer community, and although it dates way back to 2015, it’s still highly relevant today. If you want to become a developer who can innovate, not just execute, you need to understand the underlying principles of the web—not just the shortcuts. This means learning vanilla JavaScript before you move on to frameworks. In fact, understanding plain JavaScript will help you later on when it comes to deciding whether to use a framework for a certain project, and if so, which framework to use. Why Study JavaScript? JavaScript is one of the 3 languages all web developers must learn: 1. HTML to define the content of web pages 2. CSS to specify the layout of web pages 3. JavaScript to program the behavior of web pages Learning Speed In this tutorial, the learning speed is your choice. Everything is up to you. If you are struggling, take a break, or re-read the material. Always make sure you understand all the "Try-it-Yourself" examples. The only way to become a clever programmer is to: Practice. Practice. Practice. Code. Code. Code ! Commonly Asked Questions How do I get JavaScript? Where can I download JavaScript? Is JavaScript Free? You don't have to get or download JavaScript. JavaScript is already running in your browser on your computer, on your tablet, and on your smart-phone. JavaScript is free to use for everyone.
Active repository
No license
⑂ 22 forks◯ 2 issuesUpdated May 28, 2026
Angular-Directive-Project Directives range from very basic to extremely complex. This project will build up to some somewhat difficult directives. Keep in mind that the format we're learning for directives is the same format used to build some extremely complex things in angular. Using directives often and well is one way to show you're a talented developer. Starting Out We've included only a few things for you to begin with. index.html, app.js, styles.css. At this point the best way to get more comfortable with angular is to initialize an app without relying heavily on boilerplate code (reusable code that starts out your projects for you). You'll notice that in the index.html we've included the angular-route CDN. Yes, we'll be using angular's router here. Put an ng-view into your index.html. In your app.js set up a config and set up our first route for when a user is at the '/home' url. If you're having trouble remembering how to set up the router go look at how you set up the router on the previous project. One way these projects will be beneficial to you is allowing you to look back at something *you** did and seeing how you got that something to work.* You may also want add an otherwise that defaults to /home. Create a controller and a template file for this route in your app folder. Don't forget to include the controller as a script in your index.html Check that everything is hooked up correctly. Try adding a div with some text in your home template just to make sure it's showing up. Once you've got that going you're ready to start on some directives. Now let's make our directive. We'll start with a simple one that we can use to display information passed to it. Step 1. Start your directive Woot. When you're initializing your directive just remember that it works very similarly to how you start up a controller or a service. It can also be very helpful to think of your directive as a route. Create your directive. You'll use the directive method on your angular module. It takes two arguments, the name string and the callback function, which will return the object that represents your directive. When naming your directive give it a name with two words; dirDisplay would be nice, but anything works. Just remember it's best practice to give a directive a camel case name so that it's clear in your html what it is. Also we're going to need a template html for our directive. We could do it inline, but let's make another file instead. Just name it something that makes sense for the name of your directive and put it in the same directory as your directive file. For your template just make a <div> and inside a <h1> tag that says User. Now in your home route html add in your directive. It will look like this if you named it dirDisplay: <dir-display></dir-display> Start up your app and go to the home route. Check and make sure you see User where your directive was placed. If you're not seeing it at this point it could mean a few things. Here's some more common issues. You didn't link your directive in your index as a script. Your name for your directive doesn't match the name in your html. Remember camel case becomes snake case so myDirective becomes <my-directive></my-directive>. You're file path to your html template is wrong. You have to think of file paths in angular as relative to the index. Here's some code to see just for this part, and just for the directive's js file. var app = angular.module('directivePractice'); app.directive('dirDisplay', function(){ return { templateUrl: 'app/directives/dirDisplay.html' }; }); What we're returning is the directive object. You won't see anymore code in this tutorial so it's important you get things working right and refer back to what you've already done to advance from now on. Step 2. Advancing directives Your directive should be loaded up now, but it's not really doing much. Let's make it better. In your home controller. Make a variable on your $scope called user. Set it's value to { name: "Geoff McMammy", age: 43, email: "geofdude@gmail.com" } Now inside your directive's html specifically inside the <h3> tags display our new user's name. Then inside maybe some <h4> tags display his email and age. This is going to work exactly the same as if it was just inside your home controller. Reload the page and make sure it works. This is still very cosmetic and really not all that useful. It needs functionality. Add into your directive's object the link property. The link property's value is a function definition that takes (generally) three parameters. scope, element, and attributes. Unlike in other places with angular injection these parameter names don't carry meaning. The first parameter will always represent your $scope for that directive, the second will always be the element that wraps your whole directive, and the third will always be an object containing all the properties and values of the attributes on your directive in the dom. Try the following to get a feel for all three. Add two attributes to your directive in your html. Like this - <dir-display test="myTest" my-check="checkItOut"></dir-display> Now in the link property you've added console.log the three parameters in the function. You'll see an object for scope that should look identical to the $scope of your html function. For element you'll see an object the represents the DOM wrapper for your directive. For attributes you'll see an object that will look like this: { test: "myTest", myCheck: "checkItOut" } An important thing to notice is how it has again converted snake case to camel case for you. my-check became myCheck. Don't forget this. You'll run into this issue one day. It counts for both attributes and directive names. To feel some of what the link function could do let's try this. Add a ng-show to both the email and age wrappers. This should be familiar to you. Now inside your link function add a click event listener to your element property. It's going to look just like jQuery. element.on('click', function(){ }) Inside the click listener's callback add a toggle for the ng-show property you passed in. Along with a console.log to make sure things are connecting when you click. Try it out. Don't call for a mentor when it doesn't work. Let's talk about that first. You should see the console.log firing, but why isn't it toggling. This is going to be a common problem when working with the link function and event listeners. What we have here is an angular digest problem. The value is changing on the scope object, but the change isn't being reflected by our DOM. That's because angular isn't aware of the change yet. Anytime we cause an event to happen using something like jQuery or even angular's jQLite we need to let angular know that we've made a change. Add this line of code in place of your console.log, scope.$apply(). Now try it out. It should be working now, so if you're still having issues it's time to debug. What we've done is forced angular to run it's digest cycle. This is where angular checks the scope object for changes and then applies those to the DOM. This is another good lesson to learn for later. You'll most likely hit this when making changes to your element using event listeners. Step 3. Directive's re-usability. Now our directive has some extremely basic functionality. One of a directive's greatest advantages though is its ability to be placed anywhere and still be functional. Let's say instead we had a list of users instead of just one. Change the $scope property in your home controller to be users and give it this array as its value: [ { name: "Geoff McMammy", age: 43, email: "geofdude@gmail.com", city: "Provo" }, { name: "Frederick Deeder", age: 26, email: "fredeed@gmail.com", city: "Austin" }, { name: "Spencer Rentz", age: 35, email: "spencerrentz@gmail.com", city: "Sacramento" }, { name: "Geddup Ngo", age: 43, email: "geddupngo@gmail.com", city: "Orlando" }, { name: "Donst Opbie Leevin", age: 67, email: "gernee@gmail.com", city: "Phoenix" } ] Now in your home HTML add a ng-repeat to the directive call. Tell it to repeat for each user in users. Reload your page. It's working! But why? How does each directive instance know what information to display? In the link function console.log the scope parameter. Make sure it's outside of your click listener. You'll see five print outs in your console. Open up any one of them and look to the bottom. Open up the user property. It's exactly what we would want! But again why would that be the case? Don't get too caught up in this next bit if it's too hard to understand, but the ng-repeat is essentially making new tiny scope objects for each individual user in our users array. Now each of our directives is still getting a user property on the scope object just like the directive wanted in the beginning. Woot. Step 4. Ramp it up with Isolate Scope. Directives can do so much more. So let's make that happen. That means we should make.... a new directive!!! This directive's purpose will be to display a selected User and the weather in his/her/its location. Link it up just like the last one. Create a js file for our directive and name it dirWeather. Make an html file named dirWeather.html. Link it up in your index.html and add the template to your new directive object. In your directive's template give it an <h3> tag that says Weather just so we can know it's working. Above your ng-repeat on dirDisplay add your new dirWeather directive. If it's not working check the instructions above as to some common reasons why before asking a mentor for help. If you're seeing the Weather text on your page then we're ready to try out the dreaded Isolate Scope. The isolate scope object is one of the stranger API's in angular. I'm sorry but it is. Just refer to this for now. scope: { string: '@', link: '=', func: '&' } The properties on the scope object represent the attributes on the directive in the html. Our example scope object here would look something like this in the html. <example-directive string="a string" link="user" func="updateUser()"></example-directive> The hard part here is the @, =, and &. They each have very important and distinct meanings. @ says take in my attribute value as a string. = says take in my attribute value as a two-way bound variable from the parent scope. & says take in my attribute value as a reference to a function on the parent scope. It's also critical to point out that once you add a scope object you have no isolated your directive's scope. Meaning, aside from the values passed in through attributes, this directive has no connection to the $scope of its parent. That being said let's isolate our directive's scope. :worried: Add the scope property to your dirWeather. Give it the value of an object with a property of currentUser whose value is '='. Remember in your html this will look like current-user. This is the third time I've said so don't expect it again. This means that whatever comes into the currentUser attribute is going to be a value of the parent's scope object. For now test this out by passing in users[0]. Find a way to show that users information inside your dirWeather's html. Remember inside your directive now the user is represented by currentUser. Step 5. &? &!? The '=' value on your scope object has created a two-way binding between users[0] and currentUser. Now let's try out the '&'. On your home controller add a function called getWeather. It takes one parameter called city. This function will make a call to a service so we'll need to create that. Make a weather service. Name it something cool and creative like weatherService. Inside the weather service make a function called getWeather that also takes one parameter, city. Make an $http get to this url - 'http://api.openweathermap.org/data/2.5/weather?q=' After the q= add on the city parameter. If you want you can test this out in postman. See what kind of data you get back. If it's the weather of that city then... you win! Use $q to return a promise that only resolves with the data you want. Temperature (preferably not in Kelvin) and the weather description. Use console.log on the data coming from the $http request to get to what you want. You'll need to add both on an object that you resolve your new promise with. On your home controller have it return the result of invoking the get getWeather function on the service. You should be returning a promise. Now in your home route's HTML pass in the getWeather function to the dirWeather directive through an attribute called weather-call. Add the attribute to your isolate scope object. That was a lot of linking, but let's walk through it. Your controller has a function linked to the service, which is in turn linked to your directive. So if you run the weatherCall function in your directive it will go through your controller to your service and then back. Now things get a little bit tricky. Angular's way of passing along arguments through a directive to your controller are tricky, but once you understand how to do it, it's not hard. I'm going to give an example here of how it works. <my-directive pass-func="callFunc(data)"></my-directive> Here's how it would look in your HTML. But where's the data supposed to be coming from? It seems that you'd rather be able to pass in data from your directive. Well you still can, you just have to essentially tell angular what do use as an argument to replace data when it calls that function in your controller. The actualy function call inside the directive will look like this. $scope.passFunc({data: wantedData}) So what you'll do is pass in an object where the property name is what the argument is named in the HTML where you call the directive. That might sound confusing, but just look at the two code blocks above for a pattern. Note that pass-func becomes $scope.passFunc and data is being replaced with wantedData with the {data: wantedData} object. In our directive we want to replace city in the attribute call, for something else inside the directive. You'll follow the same pattern as above. For now let's get things set up for that function call. Add to the dirWeather directive object a property called controller. It's value will be a function. Yes, this is a controller specifically for your one directive. It works the same as any other controller, except you don't give it a name. It's $scope object will only be accessible within an instance of your directive. Don't forget to inject $scope in the function. Inside your controller function run the weatherCall function with the city property from the currentUser on your $scope. Here's where you need to make sure you've passed in a city argument in the attribute function call, and then replace that with your currentUser's city using an object with a city property. The function call should return a promise, so call .then afterward and add the data onto your $scope to display both the weather and temperature of the currentUser's city. The properties can be named whatever makes sense to you. You may also want to find a way to get rid of all the decimal places on your temperature. Now you should have everything hooked up so it shows Geoff's data and the weather data for Provo. But is that good enough? Step 6. Ramping up our ramp up. Now let's change this so it shows the weather data for whichever user we select. We're going to need to use '&' again. Make a function on the home controller that takes in a parameter and sets a property on the $scope to be that parameter. Maybe you see where this is going. We want to get this function into our dirDisplay controller. But in order to do that we need to isolate dirDisplay's scope. This also means we need to pass in each individual user through the scope object as well. To make it easier on ourselves, let's pass the current user from our ng-repeat into our directive through a user attribute. This way we can leave our two-way bindings as they are. Also pass our new function that sets our current user from our home controller into our directive through a setUser attribute. You'll need to add an argument in there again. Go with user. Your scope object in dirDisplay should have two properties. setUser with the value of '&' and user with the value of '='. As before we're going to need to do some tricky stuff to get our argument back to our controller. Call the setUser function inside our click event listener and pass in an object the sets our user argument to be the user on our directive's scope object. If you've forgotten this part go back up and take a look at how you did it before or the example in this README. Whatever user you click on now should show up in the dirWeather directive as the current user. But we're missing one thing, we want to be able to see the weather for that user too. We'll have to do one more thing that will seem a little bit tricky at first, but it's good to learn if you don't know it already since it's actually used quite frequently. We need to step up a change listener on our currentUser in the dirWeather directive. We'll use angular's $watch functionality. $watch is a method on your $scope that will watch for changes in a variable you give it. It works in two ways. $scope.$watch('property', function(value){ console.log("When $scope.property changes its new value is: ", value) }); And $scope.$watch(function(){ return myVar }, function(value){ console.log("When myVar changes its new value is: ", value); }); Remove the immediate function call that we have in there now. Maybe just comment it out for now because we'll use it in a bit. Now call the $watch method on your scope and have it watch currentUser. Either way of using $watch is fine. Have its callback run the $scope.weatherCall function just like you had it before. One thing to note is that $scope.$watch will always run once to begin with. Since that's what we want here it's great, but just be aware of that. If you've reached this point congratulate yourself. You've messed with some serious stuff today, namely directives. There are still a lot of things about directives that we can't possibly cover in a single project. If you like what we've done so far then you're in a good place to keep going. A developer who understands directives well can build a really clean looking code base. Just look at your home.html. It could have just two lines in it. If you're feeling good move on now to Step 7. Step 7. Finishing touches Try to work out these problems on your own. There should be a way to let the user know that the weather data is loading. Something that appears while our $http request is retrieving our data. The $http request shouldn't fire on both opening and closing a user's information. A color change for the currently active user would be nicer than showing that user's info inside the dirWeather modal. Or at least less redundant. Whatever else you want. We still haven't explored transclusion and ng-transclude so give that a try if you're feeling adventurous. Just know that it's a way for deciding where to put the HTML child elements of a directive. It's cool stuff that can involve some criss-crossing of scopes.
CSS Drills The purpose of this lab is to practice your CSS fundamentals. We'll be learning how to use and understand the position property, and how order of specificity works in CSS. Let's go! Setup Create a new project folder and connect it to a github repository. Copy the text from this README.md file into it. Create an index.html file and a styles.css file. Use the ! emmet shortcut to stub out the page. In the <head>, link the the CSS file to the HTML page. Build the HTML Structure In the body element, create a div with an id of "container" Add 4 div elements with a class name of "boxes" and a unique id (box1, box2, boxN) inside the container div. Each added div will be a child of the container div and a sibling to each other. Create an <h2>, <p>, and <a> element inside of each of the 4 div's. Add the following content to the elements: h2: Add a story title in each header p: Add a story description within each paragraph a: Add a link that says "Read More" Now, Lets style! Text and Body Styling Assign a global font family Here is a list of some Web Safe Fonts: https://www.w3schools.com/cssref/css_websafe_fonts.asp You could also play around with Google Fonts: https://fonts.google.com/ Hint: use the body selector. Change the background color for the body element to a light grey color. Use a multiple selector rule set to center the text for all h2, p, and a elements. Use an element selector to remove the default underline styles for anchor tags and change the font color: When you refresh your browser you'll notice that the link is not centered like the h2 and p elements. This is because text-align will center the element based on the width assigned to it. If you apply a border to the link tag, you'll see that the link is centered inside the anchor element. The best way to center the link, relative to its parent element, is to enclose it inside of a div element. Wrap the anchor element inside of a div and give the div a class name of "readme-wrapper" Replace the a in the multiple selector rule with .readme-wrapper Now refresh and you'll see the link is centered as we expected it Add a CSS :hover selector so that when the link is moused over the cursor changes to a pointer, the font is bold, and text color changes. Wrap a single word in each paragraph element inside of a span element and assign it a new font color and font weight. Box Styling Use a class selector to target the .boxes class: add margin add padding add a solid border with a custom color add a border radius to round the edges of the border add a width of 30em Use an id selector to add a unique background color to each div. Your lab at this point should resemble something like this, with the colors and margin/padding sizes being different for you, of course! Boxes before being re-positioned Position, how does it work?! Change the display to inline-block in your boxes class selector. Notice how the .boxes divs will now sit next to each other because of this rule. inline-block will make block level elements flow next to each other based on their size, instead of taking up the entire block! Change the display to block in your boxes class selector. Notice how it goes back to the previous flow without any display change? That's because by default div elements follow block rules by default! Cool. We're gonna leave it on block for now to do the next steps .. Change the position of the second box div to be relative to its parent. Changing that didn't seem to do anything to the flow of the page, right? Now position the second box div to be to the right of the first. Hint: try using bottom and left properties on the second box div with various values. We're "nudging" this div from where it should be in the flow of elements It won't be perfect, but it should resemble something like this, and take notice that the element flow acts as if that div is still in its original spot! We've moved this element relative to where it's supposed to be, without breaking the original flow of html elements Now change the second box div to position absolute Madness!! Notice how it moved somewhere strange, and now the other box divs act as if the second one isn't in the flow anymore Adjust your position using any of the top, bottom, left, right properties to get it back to being next to the first div. It should look something like this, and notice that the other divs flow as if the "absolute" div isn't included anymore! Remove the position styling (including all the top/bottom/left/right) from the second box div. This should make it return to what it looked like before being re-positioned. Add a new new div element in your HTML above the h2 elements in each box. These will not wrap anything, but rather be stand alone, i.e. <div></div>. These divs will be representing "images" for each box Give all four of these divs a custom class name they all share. Select that class in your CSS, and add the following properties: add a height and width property with a value of 50px add a border radius property of 50px add a custom background color You should have something close to this with those divs added and styled, with your own color, of course! Use your new knowledge of positioning to get that circle next to the h2 element! Shoot for a goal looking similar to this screenshot. It probably won't be perfect and it might scale strangely if you change your browser size, but that's okay! Raw CSS is tricky. We'll learn better ways to handle these issues later. :) Styling Specificity The following HTML code will be below the .container div, but still within your </body>. So under all the box business you've been coding so far. Below the boxes, insert 3 h1 tags into the html document, give each text for your favorite TV shows. Apply styling so that all h1’s have a font color of your choice. Add 2 more h1’s with 2 other TV shows. What immediately happens to their font color when you refresh the html doc? Give all the original 3 h1’s the class name "original" Give the additional 2 h1’s the class name "additional" Apply styling by class name, have the first 3 h1’s get a new font color (don’t delete or comment out the original styling). Once you apply a new font color by class, refresh the page and see what it does. Do the same thing for the additional 2 h1’s, give them a different font color by class name. Take note at what which font color rule is in effect. Class is more specific than element in CSS selectors! So it'll override the element selector styling and use the class selector styling. Rank each TV show you have (so all 5 h1’s), a rank of show1 would be the best. Have the rank be the id of each h1. At this point every h1 should have a class AND an id attribute. Apply styling using the id of each h1, give each a different color. Refresh the document and see how this type of styling changes what was already applied. Take note that your class selectors are now overridden by the id selectors! They are more specific and will use those styles instead of the class or element selectors on these h1's. Below the h1's, create 3 unordered lists, each with 5 items, have the first list be 5 friend's names, have the next be 5 places you want to travel, and have the last list be your favorite restaurants. Apply styling to all the unordered lists using an element selector changing their font color. Add the class "star" to the second and third unordered list. Apply styling to the class "star" changing the font color. Add the id "wars" to the third unordered list. Apply styling to the id "wars" changing the font color. Take note again on the order of specificity between element, class, and id selectors. element selector styling < class selector < id selector. Wrap each list in a div, give the div a border that is 2 pixels thick, have it be solid and the color be black. It'll look like a multi color fiasco, but the purpose here was to learn how specificity works! But isn't styling fun? We know it can be tricky and tedious, but the more you practice, the better you'll get at it. After a while, you won't even think about it anymore :)
Appsierra Expert is one of the most powerful ways for data mining the location of elements on a webpage. It is a query language used for searching and selecting nodes from the XML document. It is recommended that we should practice more and more to find XPath in the Chrome browser because we will have to find XPath in the Chrome browser when we go for any company technical test. Steps for finding XPath in Chrome Browser Google Chrome offers built-in pages and components inspection features. They’re a lot better than Firebug. You can see an XPath element on QA Services for the Selenium WebDriver script in the Chrome browser following steps. They are as follows: 1. In the Chrome browser, open the URL or web address. 2. Move your mouse over the desired web page element (object), right-click on the item you try to locate XPath, then go to the “Inspect Element” option. 3. On the right side of the screen, the element panel is now being opened. This frame displays the HTML feature code in blue…..Learn More XPath for the First name Followig are the steps: Step 1: Right-click on the element and click the inspection option, as seen in the screenshot below. Step 2: The HTML code for the “first name” feature is now shown on the blue strip. To allow DOM to search in the window, click ctrl+F. Step 3: To write the XPath for any part, follow the fundamental format. The following is the syntax for writing XPath: Syntax The path of the component at a webpage is chosen by XML path syntax. The following is provided for default syntax for creating XPath: XPath = //tagname[@Attribute = ‘value’] Where, // ➨ It select current node. Tag name ➨ It is the name of the tag of a particular node, for example, input. @ ➨ It selects attributes like id, name, className, etc. Attribute ➨ It is the node’s attribute name, for example, type, class, name, id, etc. Value ➨ It is the value of an attribute. How to find XPath in Mozilla Firefox using the Firebug add-on? Mozilla Firefox’s newer version provides integrated ways for inspecting web page elements. Nevertheless, we are using the add-on Firebug for better functionality. Follows all measures for Mozilla Firefox components with addon Firebug: 1. Install Firebug addon in Firefox 2. Moving the mouse over the appropriate element for examining an element on a list. 3. Right-click on the option “Inspect Firebug Element” to open the pop-menu. It shows tree format HTML code…..Learn More How to Inspect elements in IE browser? Microsoft Internet Explorer also provides a built-in feature, similar to Google Chrome, to inspect the webpage part. To access HTML element code in the IE browser on the web page, follow all of the steps below: 1. Click on the F12 key to open the Developer Tools. It will open the Developer Tools portion. 2. Inspect an item on the page whose HTML code is to be displayed, press the pointer icon, and pass the mouse over the requested object. 3. The developer tool displays the HTML feature code in tree style with blue contours and HTML code. Ten best XPath finder Chrome extensions Let’s see some top Chrome extensions for finding XPath in detail: Selected hub XPath in chrome and CSS selector could be returned in less than 5 seconds. It is auto-suggested by selectors hub that all attributes, text, and everything for completing selectors quickly. Now we are not required to copy and paste attribute values from DOM anymore for building XPath and CSS selector…..Learn More XPath helper We can select, edit, and evaluate XPath queries easily using XPath helper on any of the web pages. It is very simple, and the user base is more as compared to other plugins on this list. We are required to click on the extension for opening the console to write or edit the XPath. Choropleth This extension is used to generate and validate unique selectors like relative XPath, absolute XPath, Software Testing Company, link text, and partial link text with only one click…..Learn More Scraper It gets its data out of web pages and in two spreadsheets. It is a simplified extension to data mining used to promote online analysis when data needs to be filled in quickly. It is intended as an easy-to-use tool for intermediate drawing two advanced users who are comfortable with XPath. Relative XPath Helper The relative XPath Helper is being used for defining two web elements’ relative expression of XPath. Only right-click the first element and second element to get the relative XPath…..Continue Reading
<!-- markdownlint-disable no-inline-html --> <!-- markdownlint-disable ol-prefix --> # GRAVITON (aka the TEAL Blackbox Toolkit): Program Reporting and Testing via Dry Runs <img width="345" alt="http://cds.cern.ch/record/2315186/files/scoap3-fulltext.pdf" src="https://user-images.githubusercontent.com/291133/160721859-21a3560a-0a82-4249-aa54-5ede4c60f8d2.png"> **NOTE: to get math formulas to render here using Chrome, add the [xhub extension](https://chrome.google.com/webstore/detail/xhub/anidddebgkllnnnnjfkmjcaallemhjee/related) and reload** **DISCLAIMER**: Graviton is subject to change and makes no backwards compatability guarantees. ## Blackbox Testing Howto ### What is TEAL Blackbox Testing? TEAL Blackbox Testing lets you gain confidence that your Algorand smart contracts are correct by writing assertions and and analyzing results via dry runs. ### Why Blackbox Testing? Here are some use cases: * by allowing you to assert that certain invariants hold over a large set of inputs you gain greater confidence that your TEAL programs and AVM smart contracts work as designed * when tweaking, refactoring or optimizing your TEAL source, ensure that no regressions have occured * allows AVM developers to practice the art of TTDD (TEAL Test Driven Development) ## Simple TEAL Blackbox Toolkit Example: Program for $`x^2`$ Consider this [TEAL program](./tests/teal/lsig_square.teal) for computing $`x^2`$: ```plain #pragma version 6 arg 0 btoi callsub square_0 return // square square_0: store 0 load 0 pushint 2 // 2 exp retsub ``` We'd like to write some unit tests to validate its correctness and make **assertions** about the: * program's opcode cost * program's stack * stack's height * scratch variables * final log message (this is especially useful for [ABI-compliant programs](https://developer.algorand.org/docs/get-details/dapps/smart-contracts/ABI/)) * status (**PASS** or **REJECT**) * error conditions that are and aren't encountered Even better, before making fine-grained assertions we'd like to get a sense of what the program is doing on a large set of inputs and discover _experimentally_ these **program invariants**. Let's go through how we can do this: * start by making basic assertions and validate them using dry runs (see "**Basic Assertions**" section below) * execute the program on a sequence of inputs and explore the results (see "**EDRA: Exploratory Dry Run Analysis**" section below) * create invariants for the entire sequence and assert that the invariants hold (see "**Advanced: Asserting Invariants on a Dry Run Sequence**" section below) > Becoming a TEAL Blackbox Toolkit Ninja involves 10 steps as described below ### Dry Run Environment Setup **STEP 1**. Start with a running local node and make note of Algod's port number (for our [standard sandbox](https://github.com/algorand/sandbox) this is `4001`) **STEP 2**. Set the `ALGOD_PORT` value in [tests/clients.py](./tests/clients.py#L7) to this port number. (The port is already pre-set to `4001` because [graviton](https://github.com/algorand/graviton)'s [CI process](https://en.wikipedia.org/wiki/Continuous_integration) uses the standad sandbox) ### TEAL Program for Testing: Logic Sig v. App **STEP 3**. Next, you'll need to figure out if your TEAL program should be a Logic Signature or an Application. Each of these program _modes_ has its merits, but we won't get into the pros/cons here. From a Blackbox Test's perspective, the main difference is how external arguments are handled. Logic sigs rely on the [arg opcode](https://developer.algorand.org/docs/get-details/dapps/avm/teal/opcodes/#arg-n) while apps rely on [txna ApplicationArgs i](https://developer.algorand.org/docs/get-details/dapps/avm/teal/opcodes/#txna-f-i). In our $`x^2`$ **logic sig** example, you can see on [line 2](./tests/teal/lsig_square.teal#L2) that the `arg` opcode is used. Because each argument opcode (`arg` versus `ApplicationArgs`) is mode-exclusive, any program that takes input will execute succesfully in _one mode only_. **STEP 4**. Write the TEAL program that you want to test. You can inline the test as described here or follow the approach of `./tests/integration/blackbox_test.py` and save under `./tests/teal`. So following the inline appraoch we begin our TEAL Blackbox script with an <a name="teal">inline teal source variable</a>: ```python teal = """#pragma version 6 arg 0 btoi callsub square_0 return // square square_0: store 0 load 0 pushint 2 // 2 exp retsub""" ``` ### The TEAL Blackbox Toolkit's Utitlity Classes The TEAL Blackbox Toolkit comes with the following main classes: * `DryRunExecutor` - executes dry run's for apps and logic sigs for one or more inputs * `DryRunInspector` - encapsulates a dry run's result for a single input and allows inspecting and making assertions about it * `Invariant` - class for asserting invariants about a _sequence_ of dry run executions in a declarative fashion ### Basic Assertions When executing a dry run using `DryRunExecutor` you'll get back `DryRunInspector` objects. Such objects have **assertable properties** which can be used to validate the dry run. **STEP 4**. Back to our $`x^2`$ example, and assuming the `teal` variable is defined [as above](#teal). You can run the following: ```python from graviton.blackbox import DryRunExecutor from tests.clients import get_algod algod = get_algod() x = 9 args = (x,) inspector = DryRunExecutor.dryrun_logicsig(algod, teal, args) assert inspector.status() == "PASS" assert inspector.stack_top() == x**2 ``` Here we have executed a dry run on input $`x=9`$, then asserted that: * the program status was `PASS` * the program exited with the top of its stack containing $`x^2 = 9^2 = 81`$ Some available _assertable properties_ are: * `stack_top()` * `last_log()` * `cost()` * `status()` * `final_scratch()` * `error()` * `max_stack_height()` See the [DryRunInspector class comment](./graviton/blackbox.py#L387) for more assertable properties and details. ### Printing out the TEAL Stack Trace for a Failing Assertion **STEP 5**. The `DryRunInspector`'s `report()` method lets you print out a handy report in the case of a failing assertion. Let's intentionally break the test case above by claiming that $`x^2 = x^3`$ for $`x=2`$ and print out this _report_ when our silly assertion fails. ```python from graviton.blackbox import DryRunExecutor from tests.clients import get_algod algod = get_algod() x = 2 args = (x,) inspector = DryRunExecutor.dryrun_logicsig(algod, teal, args) # This one's ok expected, actual = "PASS", inspector.status() assert expected == actual, inspector.report( args, f"expected {expected} but got {actual}" ) # This one's absurd! x^3 != x^2 expected, actual = x**3, inspector.stack_top() assert expected == actual, inspector.report( args, f"expected {expected} but got {actual}" ) ``` If we run the test (e.g. with `pytest`) we'll see a printout such as: ```sh AssertionError: =============== <<<<<<<<<<<expected 8 but got 4>>>>>>>>>>> =============== App Trace: step | PC# | L# | Teal | Scratch | Stack --------+-------+------+-------------------+-----------+---------------------- 1 | 1 | 1 | #pragma version 6 | | [] 2 | 2 | 2 | arg_0 | | [0x0000000000000002] 3 | 3 | 3 | btoi | | [2] 4 | 7 | 6 | label1: | | [2] 5 | 9 | 7 | store 0 | 0->2 | [] 6 | 11 | 8 | load 0 | | [2] 7 | 13 | 9 | pushint 2 | | [2, 2] 8 | 14 | 10 | exp | | [4] 9 | 6 | 4 | callsub label1 | | [4] 10 | 15 | 11 | retsub | | [4] =============== MODE: ExecutionMode.Signature TOTAL COST: None =============== FINAL MESSAGE: PASS =============== Messages: ['PASS'] Logs: [] =============== -----BlackBoxResult(steps_executed=10)----- TOTAL STEPS: 10 FINAL STACK: [4] FINAL STACK TOP: 4 MAX STACK HEIGHT: 2 FINAL SCRATCH: {0: 2} SLOTS USED: [0] FINAL AS ROW: {'steps': 10, ' top_of_stack': 4, 'max_stack_height': 2, 's@000': 2} =============== Global Delta: [] =============== Local Delta: [] =============== TXN AS ROW: {' Run': 0, ' cost': None, ' last_log': '`None', ' final_message': 'PASS', ' Status': 'PASS', 'steps': 10, ' top_of_stack': 4, 'max_stack_height': 2, 's@000': 2, 'Arg_00': 2} =============== <<<<<<<<<<<expected 8 but got 4>>>>>>>>>>> =============== assert 8 == 4 ``` In particular, we can: * Track the program execution by viewing its **App Trace** * 2 was assigned to **scratch slot #0** at step 5 * the stack ended up with **4** on top * the run **PASS**'ed * Read the message parameter that was provided and which explains in English what went wrong: `expected 8 but got 4` ### EDRA: Exploratory Dry Run Analysis Let's expand our investigation from a single dry-run to multiple runs or a **run sequence**. We'll observe how _assertable properties_ depend on inputs and conjecture some program invariants. To aid in the investigation we'll generate a report in CSV format (Comma Separated Values) where: * columns represent _assertable properties_ of dry-runs, and * rows represents dry-run executions for specific inputs **STEP 6**. Back to our $`x^2`$ example, here's how to generate a report with 1 row for each of the inputs `0, 1, ... , 15`: ```python from graviton.blackbox import DryRunExecutor, DryRunInspector from tests.clients import get_algod algod = get_algod() inputs = [(x,) for x in range(16)] run_results = DryRunExecutor.dryrun_logicsig_on_sequence(algod, teal, inputs) csv = DryRunInspector.csv_report(inputs, run_results) print(csv) ``` Note that each element in the `inputs` array `(x,)` is itself a tuple as `args` given to a dry run execution need to be of type `Sequence` (remember, that these will be passed to a TEAL program which may take one, several, or no inputs at all). At this point, you'll be able to look at your [dry run sequence results](./graviton/blackbox.py#L752) and conduct some analysis. For the $`x^2`$ example, after loading the CSV in Google sheets and reformating a bit it will look like: <img width="465" alt="image" src="https://user-images.githubusercontent.com/291133/158812699-318169e2-487c-4dac-b97b-a9db8148b638.png"> Pointing out some interesting results: * column `D` **Arg 00** has the input $`x`$ (it's the argument at index 0) * column `A` contains the **Run** number * column `E` **top of stack** is the value at program's termination, i.e. $`x^2`$ * column `B` **Status** of each runs **PASS**es _except for **Run 1** with **Arg 00** = 0_. (The first run **REJECT**s because $`0^2 = 0`$ and TEAL programs reject when the top of the stack is 0) * column `G` shows scratch slot **s@000** which stores the value of $`x`$ (except for the case $`x = 0`$ in which appears empty; in fact, slots always default to the zero value and an **<a name="0val-artifact">artifact</a>** of dry-runs is that they do not report when 0-values get stored into previously empty slots as no state change actually occurs) * column `F` **max stack height** is always 2. The final observation makes sense because there is no branching or looping in the program. **STEP 7**. We can re-cast the observed effects in `Columns E, B, G, F` as **invariants** written in Python as follows: * `inspector.stack_top() == x ** 2` * `inspector.max_stack_height() == 2` * `inspector.status() == ("REJECT" if x == 0 else "PASS")` * `inspector.final_scratch() == ({} if x == 0 else {0: x})` ### Advanced: Asserting Invariants on a Dry Run Sequence The final and most advanced topic we'll cover is how to assert that invariants hold on a sequence of inputs. Lets take the information we gleaned in our EDRA CSV report, and create an integration test out of it. There are two ways to achieve this goal: * Procedural invariant assertions * Declarative invariant assertions #### Procedural Blackbox Dry Run Sequence Assertions **STEP 8**. The procedural approach takes the _invariants_ and simply asserts them inside of a for loop that iterates over the inputs and dry runs. One can call each dry run execution independently, or use `DryRunExecutor`'s convenience methods `dryrun_app_on_sequence()` and `dryrun_logicsig_on_sequence()`. For example, let's assert that the above invariants hold for all $`x \leq 100`$: ```python from graviton.blackbox import DryRunExecutor from tests.clients import get_algod algod = get_algod() inputs = [(x,) for x in range(101)] dryrun_results = DryRunExecutor.dryrun_logicsig_on_sequence(algod, teal, inputs) for i, inspector in enumerate(dryrun_results): args = inputs[i] x = args[0] assert inspector.stack_top() == x**2 assert inspector.max_stack_height() == 2 assert inspector.status() == ("REJECT" if x == 0 else "PASS") assert inspector.final_scratch() == ({} if x == 0 else {0: x}) ``` #### Declarative Blackbox Dry Run Sequence Assertions **STEP 9**. The TEAL Blackbox Toolkit also allows for declarative style test writing. Let's define some invariants for a particular sequence of `lsig_square` TEAL program dry runs: ```python scenario = { "inputs": [(i,) for i in range(100)], "invariants": { DRProp.stackTop: lambda args: args[0] ** 2, DRProp.maxStackHeight: 2, DRProp.status: lambda i: "REJECT" if i[0] = 0 else "PASS", DRProp.finalScratch: lambda args: ({} if args[0] else {0: args[0]}), }, } ``` In the parlance of the TEAL Blackbox Toolkit, a set of such declarative assertions is called a **test scenario**. Scenarios are dict's containing two keys `inputs` and `invariants` and follow [certain conventions](./graviton/invariant.py#L101). In particular: * **inputs** gives a list of tuples, each tuple representing the `args` to be fed into a single dry run execution * **invariants** gives a dict that maps [DryRunProperty](./graviton/blackbox.py#L25)'s to an invariant _predicate_ In English, letting $`x`$ be the input variable for our square function, the above **test scenario**: * provides a list of 100 tuples of the form $`(x)`$ that will serve as args. * IE: $`(0), (1), (2), ... , (99)`$ * establishes 4 different _invariants_ as follows: * the **stack's top** will contain $`x^2`$ * the **max stack height** during execution is always 2 * the executions' **status** is **PASS** except for the case $`x=0`$ * the **final scratch** will have $`x`$ stored at slot `0` except for that strange $`x=0`$ case (recall the [0-val scratch slot artifact](#0val-artifact)) Declarative invariants make use of the following: * `DryRunProperty` (aka `DRProp`): an enum that acts as a key in a scenario's assertions dict * class `Invariant` * its constructor takes in a predicate (there are [4 kinds of predicates](#predicate)) and returns a callable that is used for runtime assertions * method `inputs_and_assertions()` validates a scenario and extracts out its assertions * method `dryrun_assert()` evaluates the dry-run sequence using the constructed `SequenceAssertion` To employ the declarative test scenario above write the following: ```python from graviton.blackbox import ( DryRunExecutor, DryRunProperty as DRProp, ExecutionMode, ) from graviton.invariant import Invariant from tests.clients import get_algod algod = get_algod() scenario = { "inputs": [(i,) for i in range(100)], "invariants": { DRProp.stackTop: lambda args: args[0] ** 2, DRProp.maxStackHeight: 2, DRProp.status: lambda args: "REJECT" if args[0] == 0 else "PASS", DRProp.finalScratch: lambda args: ({0: args[0]} if args[0] else {}), }, } # Validate the scenario and dig out inputs/invariants: inputs, invariants = Invariant.inputs_and_invariants( scenario, ExecutionMode.Signature ) # Execute the dry runs and obtain a sequence of DryRunInspectors: inspectors = DryRunExecutor.dryrun_logicsig_on_sequence(algod, teal, inputs) # Invariant assertions on sequence: for dr_property, invariant in invariants.items(): invariant.validates(dr_property, inputs, inspectors) ``` **STEP 10**. _**Deep Dive into Invariants via Exercises**_ Four kinds of <a name="predicate">predicates</a> are used to define _invariants_: 1. _simple python types_ - these are useful in the case of _constant_ invariants. In the above `maxStackHeight` is asserted to _**ALWAYS**_ equal 2 by using `2` in the declaration: `DRProp.maxStackHeight: 2` 2. _1-variable functions_ -these are useful when you have a python "simulator" for the invariant. In the above `stackTop` is asserted to be $`x^2`$ by using a lambda expression for $`x^2`$ in the declaration: `DRProp.stackTop: lambda args: args[0] ** 2` 3. _dictionaries_ of type `Dict[Tuple, Any]` - these are useful when you want to assert a discrete set of input-output pairs. For example, if you have 4 inputs that you want to assert are being squared, you could use ```python DRProp.stackTop: { (2,): 4, (7,): 49, (13,): 169, (11,): 121, } ``` >Note that this case illustrates why each `args` container should be a tuple intead of a list. In order to specify a map from args to expected, we need to make `args` a key in a dictionary. Python dictionary keys must be hashable and lists are _not hashable_ while tuples _are_ hashable, hence the tuple-requirement. 4. _2-variable functions_ -these are useful when your assertion is more subtle than out-and-out equality. For example, suppose you want to assert that the `cost` of each run is _between_ $`2n \pm 5`$ where $`n`$ is the first arg of the input. Then you could declare: `DRProp.cost: lambda args, actual: 2*args[0] - 5 <= actual <= 2*args[0] + 5` #### **EXERCISE A** Convert each of the lambda expressions used above to dictionaries that assert the same thing. #### **EXERCISE B** Use 2-variable functions in order to _ignore_ the weird $`x=0`$ cases above. #### _PARTIAL SOLUTIONS to EXERCISES_ **Exercise A Partial Solution**. For `DRProp.status`'s declaration you could define the `dict` using dictionary comprehension syntax as follows: ```python DRProp.status: {(x,): "PASS" if x else "REJECT" for x in range(100)}, ``` **Exercise B Partial Solution**. For `DRProp.status`'s declaration you could ignore the case $`x=0`$ as follows: ```python DRProp.status: lambda args, actual: "PASS" == actual if args[0] else True, ``` ## Slow and Bad Fibonacci - Another Example Report [This](https://docs.google.com/spreadsheets/d/1ax-jQdYCkKT61Z6SPeGm5BqAMybgkWJa-Dv0yVjgFSA/edit?usp=sharing) is an example of `app_slow_fibonacci.teal`'s Dryrun stats: <img width="1231" alt="image" src="https://user-images.githubusercontent.com/291133/158705149-302d755f-afcc-4380-976a-ca14800c138f.png"> A few items to take note of: * $`n`$ is given by **Arg_00** * the app was **REJECT**ed for $`n = 0`$ because `fibonacci(0) == 0` is left at the top of the stack * the app was **REJECT**ed for $`n > 7`$ because of exceeding budget * the app **errored** only for $`n > 16`$ because of exceeding _dynamic_ budget * the **cost** is growing exponentially (poor algorithm design) * the **top of stack** contains `fibonacci(n)` except in the error case * the **final_log** contains `hex(fibonacci(n))` except in the error and reject cases * **max stack height** is $`2n`$ except for $`n=0`$ and the error case * you can see the final values of scratch slots **s@000** and **s@001** which are respectively $`n`$ and `fibonacci(n)` Here's an example of how [invariants can be asserted](https://github.com/algorand/graviton/blob/a8c7eab729a36503948849674ea55995d5fc4ec1/tests/integration/blackbox_test.py#L315) on this function.
Let's practice some HTML and CSS. We create a card summary component
Active repositoryHas homepage
CSSNo license
⑂ 0 forks◯ 0 issuesUpdated Jul 24, 2025
Project homepage ↗